Vue
一、前言
- 所谓组件化,就是将页面拆分成多个组件,每个组件依赖的 CSS、JS、模板、图片等资源放在一起开发和维护。
- 组件在系统内部可复用,组件和组件之间可以嵌套
如果项目比较复杂,使用组件可以极大简化代码量,并且对后期的需求变更和维护也更加友好。组件化提供了一种抽象,让我们可以开发一个个独立的可复用的小组件来构建我们的应用。任何的应用都会被抽象成一颗组件树。
以下引用尚硅谷张天禹老师的两张图,这很清晰明了得指出了传统编程方式的冗杂以及组件化的简化与友好
组件分为了非单文件组件与单文件组件
- 非单文件组件:一个文件中包含有 n 个组件
- 单文件组件:一个文件中只有 1 个组件
二、非单文件组件
在项目开发中,使用最多的肯定是单文件组件,但非单文件组件是单文件组件的基础,拿下非单文件组件,单文件组件岂不是分分钟的事。
1. 案例入门
Vue中使用组件的三大步骤:
- 定义组件(创建组件)
- 注册组件
- 使用组件(写组件标签)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>非单文件组件基本使用</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>{{msg}}</h1>
<hr>
<!-- 编写 school 组件标签 -->
<school></school>
<hr>
<!-- 编写 student 组件标签 -->
<student></student>
</div>
</body>
<script type="text/javascript">
// 创建school组件
const school = Vue.extend({
//组件定义时,一定不要写 el 配置项,因为最终所有的组件都要被一个 vm 管理,由 vm 决定服务于哪个容器。
template:`
<div>
<h2>学校名称:{{schoolName}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
`,
data(){
return {
schoolName:'家里蹲',
address:'重庆'
}
}
})
// 创建student组件
const student = Vue.extend({
template:`
<div>
<h2>学生姓名:{{studentName}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>
`,
data(){
return {
studentName:'划水艺术家',
age:20
}
}
})
//创建vm
new Vue({
el:'#root',
data:{
msg:'你好,划水艺术家!'
},
// 注册组件(局部注册)
components:{
school,
student
}
})
</script>
</html>
运行效果:
2. 全局注册
- 局部注册:new Vue的时候传入
components
选项 - 全局注册:
Vue.component('组件名',组件)
<script type="text/javascript">
// 创建school组件
const school = Vue.extend({
// ...
})
// 创建student组件
const student = Vue.extend({
// ...
})
//创建vm
new Vue({
el:'#root',
// ...
})
// 组件注册(全局注册)
Vue.component("school", school);
Vue.component("student ", student );
</script>
3. 组件的嵌套
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>组件的嵌套</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<app></app>
</div>
</body>
<script type="text/javascript">
//定义student组件
const student = Vue.extend({
template:`
<div>
<h2>学生名称:{{name}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>
`,
data(){
return {
name:'划水艺术家',
age:20
}
}
})
//定义school组件, school 组件里面有 student
const school = Vue.extend({
template:`
<div>
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<student></student>
</div>
`,
components:{
student
},
data(){
return {
name:'家里蹲',
address:'重庆'
}
}
})
//定义hello组件
const hello = Vue.extend({
template:`
<h1>{{msg}}</h1>
`,
data(){
return {
msg:"你好,划水艺术家"
}
}
})
//定义app组件, app 组件里面有 school、hello 组件
const app = Vue.extend({
template:`
<div>
<hello></hello>
<school></school>
</div>
`,
components:{
school,
hello
}
})
//创建vm
new Vue({
el:'#root',
components:{
app
}
})
</script>
</html>
4. 组件总结
关于组件名:
-
一个单词组成:
① 首字母小写:school
② 首字母大写:School -
多个单词组成:
① kebab-case命名:my-school
② CamelCase命名:MySchool (需要Vue脚手架支持)
备注:
- 组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行
- 可以使用
name
配置项指定组件在开发者工具中呈现的名字
关于组件标签:
① <school></school>
② <school/>
- 不使用脚手架时,
<school/>
会导致后续组件不能渲染
一个简写方式:const school = Vue.extend(options)
可简写为:const school = options
三、单文件组件
我们依然使用上述非单文件组件中的例子,只是把每一个组件列出来单独成为一个组件。
- 先看整个目录结构
- Student.vue
<template>
<div>
<h2>学生姓名:{{name}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>
</template>
<script>
export default {
name:'Student',
data() {
return {
name:'划水艺术家',
age:20
}
},
}
</script>
- School.vue
<template>
<div id='Demo'>
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<button @click="showName">点我提示学校名</button>
</div>
</template>
<script>
export default {
name:'School',
data() {
return {
name:'家里蹲',
address:'重庆'
}
},
methods: {
showName(){
alert(this.name)
}
},
}
</script>
<style>
#Demo{
background: orange;
}
</style>
- App.vue
<template>
<div>
<School></School>
<Student></Student>
</div>
</template>
<script>
import School from './School.vue'
import Student from './Student.vue'
export default {
name:'App',
components:{
School,
Student
}
}
</script>
- main.js
import App from './App.vue'
new Vue({
template:`<App></App>`,
el:'#root',
components:{
App
}
})
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>单文件组件练习</title>
</head>
<body>
<div id="root">
</div>
<script src="../js/vue.js" type="text/javascript"></script>
<script src="./main.js" type="text/javascript"></script>
</body>
</html>
当我们运行 index.html,发现什么都没有,并且控制台还报错,这是因为只有脚手架才支持组件开发。
四、Vue CLI脚手架的使用
1. 使用步骤
① 全局安装 @vue/cli
npm install -g @vue/cli
② 切换到你要创建项目的目录,然后使用命令创建项目
vue create xxxx
③ 选择使用vue的版本
④ 启动项目:npm run serve
⑤ 暂停项目:Ctrl+C
2. 分析脚手架结构
.文件目录
├── node_modules
├── public
│ ├── favicon.ico: 页签图标
│ └── index.html: 主页面
├── src
│ ├── assets: 存放静态资源
│ │ └── logo.png
│ │── component: 存放组件
│ │ └── HelloWorld.vue
│ │── App.vue: 汇总所有组件
│ └── main.js: 入口文件
├── .gitignore: git版本管制忽略的配置
├── babel.config.js: babel的配置文件
├── package.json: 应用包配置文件
├── README.md: 应用描述文件
└── package-lock.json: 包版本控制文件
3. 单文件组件案例重写
将上述的单文件案例迁移至脚手架中
① 将 School.vue 与 Student.vue 加入 components
② App.vue 引入组件并使用
③ index.html
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="UTF-8">
<!-- 针对IE浏览器的特殊配置,含义是让IE浏览器以最高渲染级别渲染页面 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 开启移动端的理想端口 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 配置页签图标 -->
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<!-- 配置网页标题 -->
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<!-- 容器 -->
<div id="app"></div>
</body>
</html>
运行效果:
五、CLI配置修改
1. 默认配置修改
- 使用
vue inspect > output.js
可以查看Vue脚手架的默认配置
vue.config.js
是一个可选的配置文件,如果项目的(和package.json
同级的)根目录中存在这个文件,那么它会被@vue/cli-service
自动加载- 使用 vue.config.js 可以对脚手架进行个性化定制,详见配置参考 | Vue CLI
module.exports = {
pages: {
index: {
// 入口
entry: 'src/index/main.js'
}
},
// 关闭语法检查
lineOnSave:false
}
2. ref属性
ref属性:
- 被用来给元素或子组件注册引用信息(id的替代者)
- 应用在html标签上获取的是真实DOM元素,应用在组件标签上获取的是组件实例对象(vc)
- 使用方式:
① 打标识:<h1 ref="xxx"></h1> 或 <School ref="xxx"></School>
② 获取:this.$refs.xxx
测试案例:
<template>
<div>
<h1 ref="title">{{msg}}</h1>
<School ref="sch"/>
<button @click="show" ref="btn">点我输出ref</button>
</div>
</template>
<script>
import School from './components/School.vue'
export default {
name:'App',
components: { School },
data() {
return {
msg:'欢迎学习Vue!'
}
},
methods:{
show(){
console.log(this.$refs.title)
console.log(this.$refs.sch)
console.log(this.$refs.btn)
}
}
}
</script>
3. mixin混入
mixin(混入):
-
功能:可以把多个组件共用的配置提取成一个混入对象
-
使用方式:
① 定义混入
export const mixin = {
data(){
....
},
methods:{
....
}
....
}
② 引入混入
局部混入:
mixins:['xxx']
全局混入:Vue.mixin(xxx)
1. 局部混入
src/mixin.js
export const mixin = {
methods: {
showName() {
alert(this.name)
}
},
mounted() {
console.log("hello~")
}
}
src/components/School.vue
<template>
<div>
<h2 @click="showName">学校姓名:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
</template>
<script>
//引入混入
import {mixin} from '../mixin'
export default {
name:'School',
data() {
return {
name:'家里蹲',
address:'重庆'
}
},
mixins:[mixin]
}
</script>
src/components/Student.vue
<template>
<div>
<h2 @click="showName">学生姓名:{{name}}</h2>
<h2>学生性别:{{sex}}</h2>
</div>
</template>
<script>
//引入混入
import {mixin} from '../mixin'
export default {
name:'Student',
data() {
return {
name:'划水艺术家',
sex:'男'
}
},
mixins:[mixin]
}
</script>
2. 全局混入
src/main.js
import Vue from 'vue'
import App from './App.vue'
import {mixin} from './mixin'
Vue.config.productionTip = false
Vue.mixin(mixin)
new Vue({
el:"#app",
render: h => h(App)
})
备注:
- 组件和混入对象含有
同名选项
时,这些选项将以恰当的方式进行“合并”,在发生冲突时以组件优先
。- 同名生命周期钩子将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用。
4. plugin插件
插件:
- 功能:用于增强Vue
- 本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据
- 定义插件:
plugin.install = function (Vue, options) {
// 1. 添加全局过滤器
Vue.filter(....)
// 2. 添加全局指令
Vue.directive(....)
// 3. 配置全局混入
Vue.mixin(....)
// 4. 添加实例方法
Vue.prototype.$myMethod = function () {...}
Vue.prototype.$myProperty = xxxx
}
- 使用插件:
Vue.use(plugin)
测试案例:
src/myPlugin.js
export default {
install(Vue, x, y, z){
console.log(x,y,z)
//全局过滤器
Vue.filter('mySlice',function(value){
return value.slice(0,4)
})
//定义混入
Vue.mixin({
data() {
return {
x:100,
y:200
}
},
})
//给Vue原型上添加一个方法(vm和vc就都能用了)
Vue.prototype.hello = ()=>{alert('你好啊')}
}
}
src/main.js
import Vue from 'vue'
import App from './App.vue'
import myPlugin from './myPlugin '
Vue.config.productionTip = false
Vue.use(myPlugin ,1,2,3)
new Vue({
el:"#app",
render: h => h(App)
})
5. scoped样式
scoped样式:
- 作用:让样式在局部生效,防止冲突
- 写法:
<style scoped>
scoped样式一般不会在App.vue中使用
测试案例:
src/components/School.vue
<template>
<div class='Demo'>
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
</template>
<script>
export default {
name:'School',
data() {
return {
name:'家里蹲',
address:'重庆'
}
},
}
</script>
<style>
.Demo{
background: orange;
}
</style>
src/components/Student.vue
<template>
<div class="Demo">
<h2>学生姓名:{{name}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>
</template>
<script>
import {mixin} from "@/mixin";
export default {
name:'Student',
data() {
return {
name:'划水艺术家',
age:20
}
},
}
</script>
<style scoped>
.Demo{
background: red;
}
</style>
src/App.vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<School></School>
<hr>
<Student></Student>
</div>
</template>
<script>
import Student from './components/Student.vue'
import School from './components/School.vue'
export default {
name: 'App',
components: {
School,
Student
},
}
</script>