这是我参与「第四届青训营 」笔记创作活动的第6天
-
组件:实现局部功能效果的代码集合(包括html,css,js,多媒体等)
-
作用:复用编码、简化项目代码、提高运行效率
-
名词解释:
- 非单文件组件:一个文件中有多个组件
- 单文件组件:一个文件中只有一个组件
基本使用(非单文件组件)
-
创建组件:
const comp = Vue.extend({ template:` template here ` name: '' data(){ return {something}; } methods:{ } components:{//嵌套的组件 } }) //简写 const comp = { template:`` ......... }
- 不能写el,管理哪一个标签最终都会由vm决定
- data必需写成函数,写成对象会报错(原因:防止复用时产生相互引用)
- 在指定了name后,不论注册时使用什么名字,开发者工具均会显示此处定义的name
-
注册组件:
//方法一(局部注册) new Vue({ components:{ name1: extend1 name2: extend2 } }) //方法二(全局注册) Vue.component('name',extend)
- 当name和extend名字相同时,可简写为一个
extend
- 当name和extend名字相同时,可简写为一个
-
使用组件
<comp_name></comp_name>
- 会将这个以组件名为标签名的特殊标签会被替换为组件内容
-
注意事项:
-
多个单词组件名
- 可以使用
xx-xx-xx
的格式,但注意在注册时需要用'xx-xx-xx'
- 可以使用例如
MySchool
格式,但必需有Vue脚手架环境的支持
- 可以使用
-
组件名不能跟原生标签名作为组件名
-
-
VueComponent:
- 组件本质是一个由Vue.extent生成的名为VueComponent的构造函数
- 在html中写
<comp_name><\comp_name>
时Vue创建对应的实例对象(相当于执行new VueComponent(options)
) - 每次调用Vue.extend返回的都是一个全新的VueComponent构造函数(不同组件互不干扰)
- this指向:组件中data、method、watch、computed中的函数调用this均指向VueComponent实例对象
-
Vue和VueComponent的关系:
Vue.prototype === VueComponent.prototype.__proto__
- 作用:Vue的组件可以访问到Vue的原型对象
单文件组件
-
文件命名:
xxx-xxx.vue
格式或XxxXxx.vue
格式 -
组件文件内容基本结构:
<template> <!--html--> </template> <script> export default Vue.extend({ name: '' data(){ } }) //可简写为 export default { } </script> <style> /*css*/ </style>
- script内需要暴露vue组件供引入
-
一般使用App组件汇总所有组件
- 作为vm唯一的子组件
- 其它所有组件均为App的后代
-
引入组件
import name from path
例如
import header from './header.vue'
脚手架入门
常用指令
-
创建项目(先切换到创建项目文件夹的父文件夹)
vue create project_name
-
运行项目(先进入项目根文件夹)
vue run serve
脚手架结构
-
文件结构
┣━━.gitignore: git配置文件 ┣━━ babel.config.js: babel的配置文件 ┣━━ package-lock.json: 包版本控制文件 ┣━━ package.json: 包的说明书 ┣━━ node_module ┣━━ public ┃ ┣ favicon.ico: 网页的页签图标 ┃ ┗ index.html: 主页面 ┗━━ src ┣ main.js: 整个项目的入口文件 ┣ App.vue: 所有组件的父组件 ┣ assets: 一般用于存放静态资源(图片、视频等) ┗ components: 放置除app以外的所有组件
-
由于脚手架中使用的vue为
vue.runtime.xxx.js
,并非完整版的js(缺失模板解析器),所以在main.js中的vm不能使用template属性,解决方法为使用rendernew Vue({ render: h => h(App)//引入App组件 })
更改脚手架结构
- 在根目录下创建
vue.config.js
vue.config.js
的写法参考官网文档
ref属性
-
功能:可以通过ref获取元素
-
使用:
-
给标签加上ref属性
<p ref="aref"></p>
-
在script中获取元素
this.$refs.aref
-
-
注意:既可以用ref获取传统html标签,也可以获取子组件标签
- html标签会获取真实DOM元素,子组件会获得VueComponent实例
props配置项
-
功能:接受外部传入组件的数据
-
语法:(以Jack传入name属性,18传入age为例)
-
传递数据:
<h1 name="Jack" :age="18"></h1>
- 若将
:age
改为age
则会导致传入的18为字符串
- 若将
-
接受:在组件的script内
-
简单接受:
prop: ['name','age']
-
限制传入数据类型
prop:{ name: String, age: Number }
-
限制类型和必要性,设置初始值
prop: { name: { type: String, required: true//必需传入,否则报错 } age: { type: Number, default: 18//如果未传入,默认值为18 } }
-
-
-
注意事项:
-
prop为只读元素,不可修改
-
如果需要对传入参数进行修改,可以将prop内的值复制进入data中,再操作data内的元素
data(){ return {aage:this.age} } prop: ['age']
-
mixin混入
-
功能:多个组件共用的配置进行提取形成一个混入对象
-
语法:
-
定义混入:把共用配置写入一个js文件(此处用mixin.js)(也可直接写作一个对象)
const mixin1 = { data(){}, methods:{} } const mixin2 = { data(){}, methods:{} }
-
导入混入对象(若不在同一个文件内)
import {mixin1,mixin2,...} from path/mixin.js
-
使用混入
-
全局使用(所有Vue实例及组件实例均可使用)
Vue.mixin(mixin1)
-
局部混入(当前实例可使用):再当前实例配置项中加上mixin配置
mixin:['mixin1','mixin2']
-
-
插件
-
功能:可以增强Vue
-
写法:
-
单独将插件的内容写入js(此处写入
plugin.js
)export default { install(Vue){ Vue.filter('',func)//定义全局过滤器 Vue.directive('',{})//定义全局指令 Vue.mixin('',{})//定义全局混入 } }
-
调用插件
import plugins from 'path.plugin.js' Vue.use(plugins)
-
scope样式
-
功能:限制样式只在当前组件生效
-
语法:在
.vue
内<script scope> </script>
不同组件之间的信息传递
-
父亲给儿子传数据:prop
-
儿子给父亲传数据
-
父亲给儿子传一个方法
-
儿子调用该方法时传入参数,由于是父亲执行该方法,即可实现父亲操作该数据
this.xxmethod()
- 此处的this.xxmethod为父亲传入的方法
-
-
注意事项:
-
v-model不要绑定props传入的值
- 若该值为对象,修改对象中的值不会直接报错,但不推荐这样的做法
-
nextTick
-
功能:等待下一次DOM更新完毕后执行回调函数
-
使用:
vc.$nextTick(function(){ })
组件化编码流程
-
拆分静态组件:按功能点拆分静态组件
-
实现动态组件:重点在于数据的存放位置
- 若只有该组件使用该数据,则存放在该组件内
- 若有多个组件使用同一个数据,则存放在他们的LCA上
-
实现交互功能:通过事件绑定
浏览器本地存储
-
存储上限:5MB左右
-
存储途径:
- window.localStorage: 本地存储,浏览器关闭不清除,手动删除才会清空
- window.sessionStorage: 浏览器关闭后会清楚
-
相关方法
-
存储
xxxStorage.setItem('key','value')
-
读取
xxxStorage.getItem('key')
-
删除
xxxStorage.removeItem('key')
-
清空
xxxStorage.clear()
-
-
tips: 通过
JSON.stringify(string)
将数据转成可读的字符串- 对应的读取函数为
JSON.parse(string)
把数据转换为其它形式(数字、对象等)
- 对应的读取函数为
自定义事件
-
功能:可以通过子组件传参实现子组件到父组件的通信
-
原理:
- 事件在子组件中触发,触发之后子组件传参
- 父组件内有对应事件的回调函数,能够接收子组件传递的参数
-
语法:
-
绑定事件
<vc v-on:事件名="xxx"></xx>
-
触发事件(在vc.vue的script中)
this.$emit('事件名')
- 触发事件时可以添加多个参数,除了第一个事件名外,其余参数会作为事件回调函数的参数
-
解绑事件
this.$off('事件名') this.$off(['事件名1','事件名2'])//解绑多个事件 this.$off()//解绑所有事件
-
-
其它写法:
-
通过ref属性实现绑定
<vc ref="aref"></vc> <script> export default { fun(){ this.$ref.aref.$on('事件名',this.xxx) } } </script>
-
此处如果直接简写为
this.$ref.aref.$on('事件名',function{})
此时该回调函数内的this指向触发事件的实例对象(一般是该段代码所在元素的子组件)
-
-
在回调函数中,使用数组接受多个参数
func(x,y,...a){//把第3个参数起后面所有参数存在数组a中 }
-
-
注意事项:
-
给组件绑定的事件默认会被当做自定义事件
<vc @click=''></vc>
- 此时click会被处理成自定义函数名
-
若要给组件绑定原生事件,应使用.native修饰
<vc @click.native=""></vc>
-
全局事件总线
-
功能:任意组件间的通信
-
原理:
- 发送至总线:将需要发送数据的组件触发的自定义事件,通过参数传数据到总线
- 从总线接收:对总线绑定绑定事件,接受发送方发送的数据
-
使用全局事件总线
-
安装全局事件总线
new Vue{ ... beforeCreate(){ Vue.prototype.$bus = this;//将全局事件总线设置为vm自身 } }
-
接收数据(一般直接在mounted里面绑定)
mounted(){ this.$bus.$on('xxx',func)//传入参数执行回调函数func就得到了参数形式的数据 }
-
发送数据
this.$bus.$emit('xxx',data1,data2,...)
-
-
注意:最好在组件的beforeDestroy中解绑当前组件所用到的事件
$off
消息订阅
-
使用的库:pubsub.js
-
该库可以在任意框架下实现消息订阅
-
安装方法
npm install pubsub-js
-
-
引入库
import pubsub from 'pubsub-js'
-
发布消息
pubsub,publish('msgName',data1,data2,...)
-
接收消息
pubsub.subscribe('msgName',function(){})
- 此时回调函数接受到的参数是发布者传递的所有参数,包括消息名
-
销毁消息订阅(最好在beforeDestroy里销毁所有订阅)
pubsub.unsubscribe(pid)
Vuex
-
官方文档:vuex.vuejs.org/zh/
-
安装:(注意:对vue2只能安装vuex3及其以下的版本)
npm install vuex@3
-
引入
import Vuex from 'vuex'
-
使用步骤:
-
在/src/store里创建index.js,并在其中配置actions、mutations、state,创建并暴露store(注意子newVuex之前要先引入组件Vue.use),index.js基本结构如下
import Vuex from 'vuex' import Vue from 'vue' Vue.use(vuex) const actions={ } const mutations={ } const state={ } const store = new Vuex.Store({ actions, mutations, state, })
-
组件调用dispatch执行action内的方法
this.$store.dispatch('actionfunc',arg1,arg2)
-
若是方法非常简单,可以跳过action直接commit
this.$store.commit('mutafunc',arg1,arg2)
-
-
配置actions:每个action内的方法都要调用commit
const actions={ add(context,value){ context.commit('ADD',value) } }
- context里面包含上下文的很多重要信息,包括commit、state、dispatch等
-
配置mutations:用于操作state内的数据
- 只有mutations内的操作才会被开发者工具捕获
-
getter
-
可以在定义Vuex实例处添加配置getter
-
类似与vuex的计算属性,可以利用state里面的属性计算出自己的值
-
用法:
-
创建(在index.js内)
const getters = { comp(state){ } } export default new Vuex.store({ ... getters })
- 能拿到一个参数为state
-
读取数据
$store.getters.comp
-
map映射实现简写
mapState和mapGetters
-
功能:辅助生成计算属性,避免
x.$store.state.xxx
或x.$store.getters.xxx
这种复杂的写法 -
映射对象
- mapState映射state内的数据
- mapGetters映射getters内的数据
-
写法:
computed:{ ...mapxxx({name1:'sname1',name2:'sname2'}) }
-
若每组的name和sname相同,可简写为
computed:{ ...mapxxx(['name1','name2']) }
-
mapMutations和mapActions
-
功能:辅助生成方法
- mapMutations帮助生成与mutations对话的方法,即含有
$store.commit
的方法 - mapActions帮助生成与actions对话的方法,即含有
$store.dispatch
的方法
- mapMutations帮助生成与mutations对话的方法,即含有
-
写法与上面两个map类似,不过定义在methods内
-
注意:如果生成的方法需要传参,一定要在绑定事件时传参(
<button @xxx='func(a,b)'><\button>
),如果不传参的话函数收到的将是事件对象event
模块化Vuex
-
将不同功能模块所共享的数据分离
-
写法:
-
在index.js中创建模块(或在其它js文件中创建之后import进入index.js)
const aAbout={ namespace: true const actions={}, const mutations={}, const state={}, const getters={}, } const bAbout={ const actions={}, const mutations={}, const state={}, const getters={}, }
- namespace为可选配置,默认为false,设置为true后才能使用命名空间
-
在index.js的Vuex中引入模块
export default new Vue.store({ modules:{ aAbout, bAbout, } })
-
组件内读取数据(在开启命名空间后)
-
state
//直接读取 this.$store.state.xxAbout.xxx //转存在data内 ...mapState('xxAbout',['xxx'])
-
getters
//直接读取 this.$store.getters['xxAbout/xxx'] //转存在comuted内 ...mapGetters('xxAbout',['xxx'])
-
调用dispatch
//直接调用 this.$store.dispatch('xxAbout/xxx',data) //转存在methods内 ...mapActions('xxAbout',['xxx'])
-
调用commit
//直接调用 this.$store.commit('xxAbout/xxx',data) //转存在methods内 ...mapMutations('xxAbout',['xxx'])
-
\
-