④ vue
1 了解VUE
- Vue.js 是一个基于
MVVM
模式的一套渐进式框架。它是以数据驱动和组件化的思想构建的,采用自底向上增量开发的设计
1.1 前端框架发展史
-
Jquery(2006)
:节点操作简单易用,浏览器兼容 -
Angular(2009)
:MVC 模式,双向数据绑定,依赖注入 -
React(2013)
:高性能(虚拟 DOM) -
Vue(2014)
:综合 angular 与 react 的优点,MVVM 模式,是一款高性能高效率的框架
1.2 架构模式
复杂的软件必须有清晰合理的架构,更容易开发、维护和测试
1.2.1 MVC
MVC
模式的意思是,软件可以分成三个部分
-
模型
Model
:数据处理 -
视图
View
:数据展示 -
控制器
Controller
:业务逻辑处理(M 和 V 之间的连接器)
-
View
传送指令到Controller
(用户发送指令) -
Controller
完成业务逻辑后,要求Model
改变状态 -
Model
将新的数据发送到View
,用户得到反馈
-
缺点:依赖复杂
-
View
依赖Controller
和Model
-
Controller
依赖View
和Model
-
1.2.2 MVP
MVP
架构模式是MVC
的改良模式(改进Controller, 把Model和View完全隔离开)
-
Model
-
View
-
Presenter
可以理解为松散的控制器,其中包含了视图的 UI 业务逻辑,所有从视图发出的事件,都会通过代理给Presenter
进行处理;同时,Presenter
也通过视图暴露的接口与其进行通信
1.2.3 MVVM
由
MVP
模式演变而来
-
Model
数据处理 -
View
数据展示 -
ViewModel
类似与MVP
中的Presenter
,唯一的区别是,它采用 双向绑定:View
的变动,自动反映在ViewModel
,反之亦然
- 核心思想:关注
Model
的变化,让MVVM
框架利用自己的机制去自动更新DOM
,从而把开发者从操作DOM
的繁琐中解脱出来!
1.3 学习 Vue 需要改变关注点
-
jquery
和原生js
的关注点:节点 -
Vue
中的关注点:数据
2 使用VUE
2.1 实例化 new Vue()
var data = { name: 'zhoutest' }
var vm = new Vue({
el: '#app',
data: data
});
2.2 常用配置选项
2.2.1 关于 DOM 节点
el
(类型:Selector|Element)
Vue
实例的挂载目标(实例中所有的属性/方法可直接在el
中直接使用),挂载元素会被Vue
生成的DOM
替换
template
(类型:String)
模板,如果不指定则以
ele
所在元素作为模板
Selector
:提取内容到template
标签,并指定选择器
render
(类型:Function)
template
的代替方案,允许你发挥js
最大的编程能力。该渲染函数接收一个createElement(tagName,props,children)
方法作为第一个参数用来创建VNode
- 优先级:
render
>template
>el.outerHTML
new Vue({
//...
el:'#app',
template:`<div>{{username}}</div>`,
render:createElement=>{
return createElement('h1',{title:'标题',class:'title'},'文章标题')
}
})
2.2.2 关于数据
data
(类型:Object|Function)
Vue 实例化时,它将
data
中所有的属性添加到 响应式系统 中,当这些数据改变时,视图会进行重渲染
data
下的属性会自动成为vm
下的属性:Vue
在实例化时,会遍历data
下所有属性,把他们变成响应式属性
computed
(类型:Object)
对于需要复杂逻辑或运算才能得到的值,应当使用计算属性
methods
(类型:Object)
一般用于编写公共方法、事件处理函数等,方法中的
this
指向实例,所以不应该使用箭头函数来定义method
函数
watch
(Object)
监听属性(Function),监听的值被修改时会自动调用函数,当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的
watch: {
username: function (val, oldVal) {
console.log('new: %s, old: %s', val, oldVal)
}
}
2.3 实例属性&方法
Vue 实例化时,会遍历
data/computed/methods
中所有属性/方法,并写入Vue的实例
2.3.1 属性特性
1. 值属性(有自己的值的属性)
-
configurable
可配置性(属性特性总开关) -
enumerable
可枚举性(是否可遍历) -
writable
可写性 -
value
2. 存储器属性(本身没有值,一般用于代理其他数据)
-
configurable
可配置性(属性特性总开关) -
enumerable
可枚举性(是否可遍历) -
get
监听读取操作 -
set
监听写入操作
传统方式设置的属性,所有的属性特性默认为
true
;而通过Object.defineProperty()
设置的属性特性默认为 false
3. 设置属性特性
-
Object.defineProperty(obj, key, descriptor)
-
Object.defineProperties(obj, descriptors)
Object.defineProperties(user,{
age:{
configurable:true
value:18
},
password:{}
})
4. 获取属性特性
-
Object.getOwnPropertyDescriptor(obj, key)
-
Object.getOwnPropertyDescriptors()
2.3.2 响应式属性
-
Vue 在实例化时,会自动遍历 data 下的所有属性,并通过
Object.defineProperty()
把他们变成存储器属性,并写入 Vue 的实例 -
特点:对属性的修改 UI 会自动更新
-
原理:
getter & setter
当这些属性值发生改变时,视图将会产生“响应”,及匹配更新为新的值
let data = {
username:'zhoutest',
age:18,
password:123456
}
let vm = new Vue({
el:'#app'
data
})
-
从
new Vue
开始,首先通过get、set
监听Data
中的数据变化,同时创建Dep
用来搜集使用该Data
的Watcher
-
编译模板,创建
Watcher
,并将Dep.target
标识为当前Watcher
-
编译模板时,如果使用到了
Data
中的数据,就会触发Data
的get
方法,同时调用Dep.addSub
将Watcher
搜集起来 -
数据更新时,会触发
Data
的set
方法,然后调用Dep.notify
通知所有使用到该Data
的Watcher
去更新DOM
设置响应式属性
-
设置初始化数据
data
-
Vue.set(target,key,val)
向__响应式系统__中的对象添加属性并自动渲染视图注意:
target
对象不能是 Vue 实例,或者 Vue 实例的根数据对象 -
数组变异方法
2.3.3 内置属性
除了数据属性,Vue 实例还提供了一些有用的实例属性与方法。它们都有前缀
$
,以便与用户定义的属性区分开来
-
$data
: 同 data -
$el
: 同 el节点 -
$refs
-
$parent
-
$children
-
$root
2.3.4 内置方法
-
数据 data
-
$watch()
:监听数据变化,同 watch 配置选项 -
$set()
:Vue.set() 的别名
-
-
事件 event
-
$on()
:监听当前实例上的自定义事件 -
$off()
:移除自定义事件监听器 -
$emit()
:触发当前实例上的事件
-
-
生命周期函数
2.4 指令 directive
- 指令是带有
v-*
前缀的特殊属性,格式:v-指令名:参数.修饰符="值"
2.4.1 内置指令
1. 数据绑定
单向数据绑定
-
{{}}
:插值表达式差值表达式中应当放置一些简单的运算(data 中的数据、函数执行、三元运算等),对于任何复杂逻辑,你都应当使用计算属性操作完成后再写入插值表达式
-
v-text
:显示文本 -
v-html
:显示 html 内容 -
v-bind
:
- 可绑定任意属性
<img v-bind:src="imgurl">
<!-- 简写 -->
<img :src="imgurl">
- 对 style 与 class 的绑定
在将
v-bind
用于 class 和 style 时,Vue 做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组
<div class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }"
v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }">
</div>
<script>
new Vue({
data: {
isActive: true,
hasError: false,
activeColor: 'red',
fontSize: 30
}
})
</script>
<!-- 最终结果:<div class="static active" style="color:red;font-size:30px"></div> -->
v-bind
无参数绑定对象
<div v-bind="{username:'laoxie',age:18,gender:'男'}"></div>
<!-- 等效于 -->
<div v-bind:username="laoxie" v-bind:age="18" v-bind:gender="男"></div>
v-model
双向数据绑定
v-model
一般用于表单元素,会忽略所有表单元素的 value、checked、selected 特性的初始值而总是将 Vue 实例的数据作为数据来源
-
v-model
值绑定到 value 属性-
单行文本框
text
-
多行文本框
textarea
-
单选框
radio
-
选择框
select
(无 value 属性则与内容绑定) -
复选框
checkbox
-
初始值为数组,与 value 属性绑定
-
初始值为其他,与 checked 属性绑定(true,false)
-
true-value:设置选中时的值
-
false-value:设置补选中时的值
-
-
-
修饰符
-
lazy
:input 触发改成 change 触发 -
number
:输出值为 number 类型 -
trim
:自动清楚前后空格
-
-
双向数据绑定原理
-
Model -> View
:响应式属性 -
View -> Model
:事件
-
-
v-model
的原理(替代方案)-
v-bind:value="val"
-
v-on:input="val=$event.target.value"
组件中使用 v-model 等效于:
v-on:input="val=arguments[0]"
-
列表渲染
-
v-for
可遍历
Array
|Object
|number
|string
|Iterable
- 遍历数组
<li v-for="(value, index) in arr">{{value}}</li>
- 遍历对象
<tr v-for="(value, key, index) in obj"> <td>{{index+1}}</td> <td>{{key}}-{{value}}</td> </tr>
-
key
:Vue 识别 DOM 节点的一个通用机制(用于 diff 算法)-
Vue 对相同的元素进行展示排序等操作时,遵循“就地复用”原则,因为这样更加高效,性能更好
-
但对于依赖子组件状态或临时 DOM 状态 (如:表单输入值、复选框选中等)的列表,会出现操作混乱的问题
-
指定 key 属性后,意为去掉“就地复用”特性(建议尽可能在使用 v-for 时提供 key)
-
2. 显示隐藏
-
v-show
(频繁显示隐藏)通过
display
属性控制元素的显示隐藏 -
v-if
|v-else
|v-else-if
(不频繁的显示隐藏)通过创建/移除的方式控制元素的显示隐藏
影响页面性能几大因素
-
节点的频繁操作
- 判断一段
js
代码所花的时间console.time(name); | console.timeEnd(name);
- 判断一段
-
事件绑定数量
Virtual DOM
一个结构类似与真实
DOM
节点的js
对象
-
优化方式
-
优化节点操作
-
优化事件处理
-
-
虚拟
DOM
是怎么优化性能的:背后有一套强大的算法:diff算法
-
只改变对象初始 | 结束状态不同部分
-
diff
算法如何比较同级同类标签的不同 -- 复用原则
-
-
key
:唯一且稳定
// 虚拟DOM大概样子
{
type:'div',
attrs:{},
children:[{
type:'h1',
children:'2021'
},{
type:'ul',
children:[{
type:'li',
children:'1111'
}.{
type:'li',
children:'2222'
},{
type:'li',
children:'3333'
}]
}]
}
3. 事件绑定
- 格式:
v-on
:事件类型.修饰符="事件处理函数"
event 对象的获取:在事件触发时自动写入 vm.$event
事件修饰符
-
stop
-
prevent
-
capture
-
self
:只当在event.target
是当前元素自身时触发处理函数(e.target === e.currentTarget
) -
once
事件将只会触发一次 -
按键修饰符
- 直接使用键码来作为按键修饰符
<!-- 只有在 `keyCode` 是 13 时调用 `vm.submit()` --> <input v-on:keyup.13="submit">
- 使用别名作为按键修饰符
left
up
right
down
enter
tab
esc
space
2.4.2 自定义指令
1. 全局指令
-
格式:
Vue.directive(name, option)
-
参数
-
name
:指令名字,使用格式:v-name -
option
-
Object
:放钩子函数 -
Function
:默认为bind和update的钩子函数
-
-
2. 局部指令
- 格式:
directives: {}
// 使用指令:v-zhoutest
Vue.directive('zhoutest', {
bind: function (el, binding, vnode) {
//binding参数如下
el.innerHTML =
'name: ' + JSON.stringify(binding.name) + '<br>' + //指令名
'value: ' + JSON.stringify(binding.value) + '<br>' + //指令值
'expression: ' + JSON.stringify(binding.expression) + '<br>' + //字符串形式的指令表达式
'arg: ' + JSON.stringify(binding.arg) + '<br>' + //指令参数,
'modifiers: ' + JSON.stringify(binding.modifiers) + '<br>' //指令修饰符
}
});
2.5 生命周期函数
beforeCreate()
-
初始化完成,但为往实例添加属性
-
应用:可以在这加个loading事件
created()
- 应用:在这结束loading,还做一些初始化,实现函数自执行
beforeMount()
-
可以获取节点,但数据未渲染
-
应用:在这发起ajax请求,拿回数据,配合路由钩子做一些事情
mounted()
实例挂载到 DOM
- 应用:节点操作
beforeUpdate()
- 数据有更新但未更新节点
updated()
- 更新节点完毕
beforeDestroy()
destroyed()
执行destroy()后,不会改变已生成的DOM节点,但后续就不再受vue控制了
- 应用:清除定时器、延迟器、取消ajax请求等
2.6 过滤器
Vue 允许你自定义过滤器,可被用于一些常见的文本格式化
- 过滤器可以用在两个地方:
双花括号插值
和v-bind
<!-- 在双花括号中 -->
{{ message | capitalize }}
<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>
全局过滤器
格式:
Vue.filter(name, definition)
局部过滤器
格式:
filters 属性
// 首字母大写
Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
2.7 mixin
混入
混入一般用于组件选项的复用,并以一定的合并规则混入到组件中
全局 mixin:Vue.mixin(options)
全局注册一个混入,会影响后面所有创建的每个 Vue 实例/组件(影响较大,一般用于插件编写)
Vue.mixin({
created: function () {
// created生命周期函数会混入到下面的Vue实例中,且不会影响原来的选项
console.log('global mixin:', this.username)
}
});
new Vue({
data:{
username:'zhoutest'
},
created(){
console.log('app.username', this.username)
}
});
局部 mixins:mixins:[mymixin]
一般用于提取多个组件的公有部分配置
var mixin = {
data: function () {
return {
message: 'hello',
foo: 'abc'
}
}
}
new Vue({
mixins: [mixin],
data: function () {
return {
message: 'goodbye',
bar: 'def'
}
},
created: function () {
console.log(this.$data);// => { message: "goodbye", foo: "abc", bar: "def" }
}
})
2.8 开发插件
插件可以是一个对象(必须提供
install
方法),也可以是一个函数,它会被作为install
方法,并把 Vue 作为参数传入
2.8.1 插件类型
-
添加全局方法或者属性,如:
vue-custom-element
-
添加全局资源:指令/过滤器/过渡等,如
vue-touch
-
通过全局 mixin 方法添加一些组件选项,如:
vue-router
-
添加 Vue 实例方法,通过把它们添加到
Vue.prototype
上实现。 -
一个库,提供自己的 API,同时提供上面提到的一个或多个功能,如
vue-router
MyPlugin.install = function(Vue, options) {
// 1. 添加全局方法或属性
Vue.myGlobalMethod = function() {
// 逻辑...
}
// 2. 添加全局资源
Vue.directive('my-directive', {
bind (el, binding, vnode, oldVnode) {
// 逻辑...
}
...
})
// 3. 注入组件(影响后面定义的所有组件)
Vue.mixin({
created: function () {
// 逻辑...
}
...
})
Vue.component('mycomponent',{
// 继承mixin中的created等配置
})
// 4. 添加实例方法
Vue.prototype.$myMethod = function (methodOptions) {
// 逻辑...
}
}
2.8.2 使用
通过全局方法
Vue.use()
使用插件。它需要在你调用new Vue()
启动应用之前完成
Vue.use(MyPlugin);//会自动调用MyPlugin中的install方法
new Vue({
//... options
})
3 组件 Component
- 优点:代码复用 & 便于维护
3.1 组件定义和使用
组件是可复用的
Vue
实例,带有一个名字,所以它们与new Vue
接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等(el 选项除外)
3.1.1 组件要求
-
data 必须为
Function
类型 -
每个组件必须只有一个根元素,否则报错
-
注册时组件名可以是
kebab-case
或PascalCase
,但在html页面上使用时,必须写成遵循W3C 规范中的自定义组件名 (字母全小写且必须包含一个连字符)
3.1.2 定义
1. 全局组件
-
Vue.component(name, options)
,所有的 Vue 实例都可以使用 -
类似于
new Vue(options)
Vue.component('my-component', {
// ... options ...
template:'<p>我是全局组件</p>'
})
2. 局部组件
- 在某个Vue实例中通过
components
属性注册的组件为局部组件,只有当前实例能使用
var Child = {
data() {
return {
name: '我是局部组件'
}
},
template: '<h1>hello, {{name}}</h1>'
}
// 创建根实例
new Vue({
el: '#app',
components: {
Child
}
});
3.1.3 使用
- 使用组件时,组件
template
中的内容会替换调组件所在位置
<div id="app">
<my-component></my-component>
<child></child>
</div>
- 注意:由于 Vue 只有在浏览器解析和标准化 HTML 后才能获取模板内容,所以把组件嵌套在某些特定的元素(如
table, ul, ol, select
等)上时,会导致解析错误
<!-- table为已经存在html页面上的元素 -->
<table>
<my-row></my-row>
</table>
Vue.component('my-row',{
template:'<tr><td>test</td></tr>'
});
- 以上解析的结果为,tr 被解析到了 table 外面,解决方式也很简单,利用特殊的
is
属性实现
<table>
<tr is="my-row"></tr>
</table>
3.2 组件通讯
原则:谁的数据谁修改
3.2.1 父组件 -> 子组件:props
-
组件实例的作用域是孤立的
-
要让子组件使用父组件的数据,需要通过子组件的
props
选项
1. 步骤
-
在子组件上定义属性,并传递数据
-
在子组件中通过
props
配置参数接收数据
接收到的数据会自动称为子组件的属性
2. props
声明属性
-
声明的属性会自动成为组件实例的属性(可通过
this.xx
访问) -
prop
传递是单向的,当父组件的属性变化时,将传导给子组件,但是不会反过来
<blog-post mytitle="静态数据"></blog-post>
Vue.component('blog-post', {
props: ['mytitle'],
template: '<h3>{{ mytitle }}</h3>'
})
<blog-post :msg="message"></blog-post>
<!-- 传入一个对象 -->
<blog-post v-bind:author="{ name: 'zhoutest', age:18 }"></blog-post>
Vue.component('blog-post', {
props: ['msg','author'],
template: '<h3>{{ msg }}</h3>'
});
let vm = new Vue({
data:{
message:'hello zhoutest'
}
})
3. 非 props 属性
- 此类属性会自动成为组件根节点的属性(可通过
{inheritAttrs: false}
关闭)
4. prop
数据验证
对传入的
prop
属性进行校验,如:数据类型、必填、默认值等
Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 匹配任何类型)
propA: Number,
// 多个可能的类型
propB: [ String, Number ],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字,无prop属性传入时,默认得到100
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
myscore: {
validator: function (value) {
// 这个值必须大于等于60,否则报错
return val >= 60
}
}
}
})
3.2.2 子组件 -> 父组件:自定义事件 + $emit
- Vue 遵循 单向数据流 原则,不允许在子组件中直接修改
props
传入的父组件数据,可以通过自定义事件系统,利用$emit()
方法触发父组件函数来达到修改的效果
1. 方法①
-
在子组件上定义一个事件
v-on:additem
-
在子组件内部触发这个自定义事件:
$emit()
2. 方法②
-
可以利用
v-bind:xx.sync
修饰符(如下color属性) -
子组件调用
this.$emit('update:xx',val)
触发更新
<div id="app">
<p :style="{fontSize:fontSize+'px'}">字体大小:{{fontSize}}</p>
<btn-change :font-size="fontSize" @bigger="updateFontSize" :color.sync="color"></btn-change>
</div>
<template id="myButton">
<button @click="changeSize">改变字体大小</button>
<button @click="changeColor">改变字体颜色</button>
</template>
<script>
new Vue({
el:'#app',
data:{
fontSize:16,
color:'red'
},
components:{
btnChange:{
props:['fontSize'],
template:'#myButton',
methods:{
changeSize(){
this.initFontSize++;
// 手动触发自定义事件并传递修改后的值
this.$emit('bigger',this.fontSize+1);
},
changeColor(){
this.$emit('update:color','#58bc58');
}
}
}
},
methods:{
updateFontSize(val){
// 触发自定义事件的事件处理函数,并修改字体大小
this.fontSize = val;
}
}
})
</script>
3.2.3 兄弟组件通信
- 组件A -> 父组件 -> 组件B
3.2.4 多层级组件通讯
- 利用一个 Vue 实例作为中间桥梁实现传参
事件总线 Bus
- 自定义事件
接收方自定义事件
$on()
- 触发自定义事件
发送方触发事件
$emit()
// 定义中间桥梁bus
let bus = new Vue();
//组件A
let comA = {
data(){
return {
msg:'I am A'
}
},
template:`<div>
<p>{{msg}}</p>
<button @click="send">传数据到B组件</button>
</div>`,
methods:{
send(){
bus.$emit('data',this.msg);
}
}
}
// 组件B
let comB = {
data:()=>({
msg:'I am B'
}),
mounted(){
bus.$on('data',val=>this.msg = val)
},
template:`<div><p>{{this.msg}}</p></div>`
}
// 创建实例,并注册子组件
new Vue({
el:'#app',
components:{
comA,
comB
}
});
provide
+ inject
-
爷:定义
provide
对象传递 -
孙:使用
inject
接收
3.3 内置组件
<component>
动态组件
is
:指定渲染的组件
<component v-bind:is="currentTabComponent"></component>
<keep-alive>
缓存组件
把切换出去的组件保留在内存中,可以保留它的状态或避免重新渲染可以添加一个 keep-alive
包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们,主要用于保留组件状态或避免重新渲染
-
include
(String/Regexp):指定缓存组件名 -
exclude
(String/Regexp):指定不缓存的组件名
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>
<slot>
内容分发
3.4 插槽
- 内容通讯:插槽
slot
3.4.1 利用组件内容进行通讯(组件外 -> 组件内)
- 在组件模板中利用内置组件
<slot></slot>
来承载组件内容,否则它的内容都会被忽略
默认插槽:<slot>
<!-- 使用组件 -->
<nav-link url="/home">首页</nav-link>
<!-- 定义组件 -->
<script>
Vue.component('nav-link',{
props:['url']
template:`<a :href="url"><slot></slot><span>Home</span></a>`
});
</script>
具名插槽:<slot name="xx">
-
模板内容:给
<slot/>
组件命名(设置name属性) -
组件内容:设置 slot 属性
v-slot:name
,实现内容精准显示到模板具体位置
<!-- 组件模板内容 -->
<template id="myTest">
<div>
<slot name="header">这是拥有命名的slot的默认内容</slot>
<slot>这是拥有命名的slot的默认内容</slot>
<slot name="footer">这是拥有命名的slot的默认内容</slot>
</div>
</template>
<!-- 使用组件 -->
<my-component>
<template v-slot:header>这里的内容显示到name为header的插槽</template>
<span>这里的内容显示到默认插槽</span>
<template v-slot:footer>这里的内容显示到name为footer的插槽</template>
</my-component>
3.4.2 作用域插槽(组件内 -> 组件外)
-
把需要传递的参数写入 slot 属性
-
v-slot="scope"
(scope为写入solt的所有属性组成的对象)
<!-- mynav组件模板 -->
<div class="box">
<slot :msg="msg" :username="username">{{username}}, {{msg}}</slot>
<slot name="footer" title="播放器" :player="player">{{player}}</slot>
</div>
<!-- 组件内容 -->
<mynav>
<!-- props为传过来的数据组成的对象 -->
<div slot-scope="props">{{props.msg}}, {{props.username}}</div>
<!-- Vue2.6+用法 -->
<div v-slot:default="props">{{props.msg}}, {{props.username}}</div>
<div v-slot:footer="foot">{{foot.title}}, {{foot.player}}</div>
</mynav>
4 模块系统
4.1 前言
常规的定义组件的方法在复杂的项目中存在以下缺点
-
全局定义 强制要求每个
component
中的命名不得重复 -
字符串模板 缺乏语法高亮,在 HTML 有多行的时候,需要用到丑陋的 \
-
不支持 CSS 意味着当 HTML 和 JavaScript 组件化时,CSS 明显被遗漏
-
没有构建步骤 限制只能使用 HTML 和 ES5 JavaScript, 而不能使用预处理器,如 Pug (formerly Jade) 和 Babel
4.2 vue 单文件组件
-
Vue 单文件组件(扩展名为 .vue),由于浏览器不支持
.vue
文件, 和ES6的模块化(import
,export
)开发, 必须利用babel
和webpack
工具来辅助实现编译成浏览器支持的格式 -
vue单文件优点
-
完整语法高亮
-
CommonJS 模块
-
组件作用域的 CSS
-
4.3 vue 单文件组件开发流程
webpack打包
4.3.1 安装必要模块
"devDependencies": {
"html-webpack-plugin": "^3.2.0",
"vue": "^2.5.17",
"vue-loader": "^15.4.2",
"vue-template-compiler": "^2.5.17",
"webpack": "^4.18.0",
"webpack-cli": "^3.2.3"
}
4.3.2 设置 webpack 配置文件(webpack.config.js)
4.3.3 应用入口文件(app.js)
//ES6 引入其他模块(可以是js,css,vue单文件等)
import Vue from 'vue';
import App from './app.vue';
new Vue({
el:'#app',
// render 函数若存在,则忽略 template 或 el 元素中的内容
render(create){
return create(App);
}
});
4.3.4 单文件组件(app.vue)
<template>
<div class="container">
<h1>Hello {{name}}</h1>
<button class="btn btn-success">点击进入</button>
</div>
</template>
<script>
// 导出当前组件配置选项
export default{
data(){
return{
name:'Vue单文件组件'
}
}
}
</script>
<!-- 设置具有组件作用域的样式 -->
<style scoped>
h1{color:#58bc58;}
</style>
4.4 ES Module
浏览器和服务器通用的模块解决方案,完全可以取代
CommonJS
和AMD
规范
4.4.1 基本特点
-
每一个模块只加载一次, 并执行一次,再次加载同一文件,直接从内存中读取
-
每一个模块内声明的变量都是局部变量, 不会污染全局作用域
-
通过
export
导出模块,通过import
导入模块 -
ES6模块只支持静态导入和导出,只可以在模块的最外层作用域使用
import
和export
4.4.2 export
export
命令用于规定模块的对外接口,只允许导出最外层函数、类以及 var、let 或 const 声明的变量,可多次 export,export 出去后自动成为 模块对象的属性
export
后只能跟function
、class
、var
、let
、const
、default
、{}
1. 基本用法
//base.js
var myName = 'laoxie';
var age = 1995;
// 多次export
export { myName };
export let gender = "男";
export function show() {
console.log(666);
}
2. as
- 通常情况下,
export
输出的变量就是本来的名字,但是可以使用as
关键字重命名
function show() {
console.log('my name is show');
}
export { show as showName };
3. default
- 为模块指定默认输出,这样就可以在使用
import
指令的时候,不必知道所要加载的变量名或函数名
export default {
data:{
path:'/src/'
}
}
4. *
作为中转模块导出,把某个模块的所有相属性/方法导出
export * from './md.js';
4.4.3 import
-
import
命令用于导入其他模块提供的功能 -
格式:
import <module> from <url>
1. url 支持格式
// 支持
import base from 'http://laoxie.com/js/base.js';
import base from '/js/base.js';
import base from './base.js';
import base from '../base.js';
// 不支持
import base from 'base.js';
import base from 'js/base.js';
2. 基本用法
//从模块对象中导入属性为default的值,并赋值给变量res,无则得到undefined
import res from './base.js';
//导入模块对象中属性为myName的值并赋值给变量myName
import { myName } from './base.js';
3. as
修改变量名
//导入模块对象中属性为myName的值并赋值给变量username
import { myName as username } from './base.js';
4. *
导入整个模块对象
//导入整个模块对象,并赋值给myModule变量
import * as myModule from './base.js';
4.5 在 html 中使用 ES Module
浏览器支持 ES Module
在
<script>
标签中指定type="module"
<script type="module">
import res from './base.js';
console.log(res)
</script>
<script type="module" src="js/base.js"></script>
浏览器不支持 ES Module
利用
webpack
等工具转换成ES5后引入(推荐)
5 过渡动画
<transition>
<transition-group>
<transition>
用于单个元素动画,<transition-group>
用于多个元素并解析为一个标签(默认:span)
5.1 属性
-
name
: 过渡类名前缀(默认:v)如设置 name="fade",过渡类名变成:fade-enter / fade-enter-active /fade-leave / fade-leave-active
-
css
: boolean,是否使用 CSS 过渡类(默认:true)。设置为 false,将只通过组件事件触发注册的 JavaScript 钩子。
自定义过渡类名(可配合 animate.css 框架实现过渡效果)
-
enter-class
-
enter-active-class
-
enter-to-class
-
leave-class
-
leave-active-class
-
leave-to-class
<transition
enter-active-class="bounceIn"
leave-active-class="bounceOut"
>
</transition>
5.2 触发动画场景
前提条件(缺一不可)
-
状态改变 --> 通过样式 | js改变
-
使用
<transition> | <transition-group>
单元素 | 单组件 -
动画元素必须有进入状态、离开状态
如何让组件发生进入状态、离开状态?
-
条件渲染 (使用 v-if)
-
条件展示 (使用 v-show)
-
动态组件
-
组件根节点
5.3 CSS 过渡
通过CSS过渡类名
-
v-enter
:进入过渡的开始状态,元素被插入时生效,只应用一帧后立即删除 -
v-enter-active
:进入过渡的结束状态,元素被插入时就生效,在过渡过程完成之后移除 -
v-leave
:离开过渡的开始状态,元素被删除时触发,只应用一帧后立即删除 -
v-leave-active
:离开过渡的结束状态,元素被删除时生效,离开过渡完成之后被删除
5.4 js 过渡
通过内置事件实现过渡动画效果,可以利用第三方动画库(如:velocity.js,jquery等)实现动画效果
<transition
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:after-enter="afterEnter"
v-on:enter-cancelled="enterCancelled"
v-on:before-leave="beforeLeave"
v-on:leave="leave"
v-on:after-leave="afterLeave"
v-on:leave-cancelled="leaveCancelled"
>
</transition>
methods: {
// 过渡进入
// 设置过渡进入之前的组件状态
beforeEnter: function (el) {
// ...
},
// 设置过渡进入完成时的组件状态
enter: function (el, done) {
// ...
done()
},
// 设置过渡进入完成之后的组件状态
afterEnter: function (el) {
// ...
},
enterCancelled: function (el) {
// ...
},
// 过渡离开
// 设置过渡离开之前的组件状态
beforeLeave: function (el) {
// ...
},
// 设置过渡离开完成时地组件状态
leave: function (el, done) {
// ...
done()
},
// 设置过渡离开完成之后的组件状态
afterLeave: function (el) {
// ...
},
// leaveCancelled 只用于 v-show 中
leaveCancelled: function (el) {
// ...
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)