Vue
Vue
组件化常用技术// child
props:{msg:String}
// partent
<Child msg='hello world'></Child>
// parent
<Child ref='child'></Child>
this.$refs.child.xx
// child
this.$emit('child','child message')
// parent
<Child @child='showChildMessage'></Child>
通过共同的祖辈组件搭桥, p a r e n t 或 parent或 parent或root
// brother1
this.$parent.$on('brotherFun',brotherMessage=>{console.log(brotherMessage)})
// brother1
this.$parent.$emit('brotherFun','brotherMessage')
// ancestor
provider(){
return {
message:'hello world!'
}
}
// descendant
inject:['message']
// 定义一个dispatch方法
function dispatch(eventName,data){
let parent=this.$parent
// 只要还存在父元素就继续往上找
while(parent){
// 父元素用$emit触发
parent.$emit(eventName,data)
// 递归查找父元素
parent=parent.$parent
}
}
// 使用 descendant
<h1 @click="dispatch('message','hello world!')">给祖先传值</h1>
// ancestor
this.$on('message',message=>{console.log(message)})
// Bus:事件派发、监听和回调管理
class Bus{
constructor(){
this.callbacks={}
}
$on(name,fn){
this.callbacks[name]=this.callbacks[name] || []
this.callbacks[name].push(fn)
}
$emit(name,args){
if(this.callbacks[name]){
this.callbakcs[name].forEach(cb=>cb(args))
}
}
}
// main.js
Vue.prototype.$bus=new Bus()
// child1
this.$bus.$on('message',message=>{
console.log(message)
})
// child2
this.$bus.$emit('message','hello world')
// child
<div>
<slot><slot>
</div>
// parent
<Child>hello world!</Child>
Vue2.6.0
之后采用全新的v-slot语法取代之前的slot、slot-scope
// child
<div>
<slot></slot>
<slot name='content'></slot>
</div>
// parent
<Child>
<!-- 默认插槽用default做参数 -->
<template v-slot:default>默认插槽</template>
<template v-slot:content>内容部分...</template>
<Child>
// child
<div>
<slot :message='hello world'></slot>
</div>
// parent
<Child>
<template v-slot:default='child'>
来自子组件的数据:{child.message}
</template>
</Child>
v-model
// v-model 是语法糖
<Input v-model='username'>
// 默认等效于下面代码
<Input :value='username' @input='usernameHandle'>
.sync
// sync修饰符类似于v-model,他能用于修改传递到子组件的属性
<Input :value.sync='username'>
// 等效于下面代码
<Input :value='username' @update:value='usernameHandle'>
Vue-router
使用routes:[
{
path:'/',
name:'home',
component:Home
},
{
path:'/about',
name:'about',
// 路由层级的代码分割,webpack打包时会生成 about.[hash].js 独立文件
// 路由访问时模块会懒加载
component:()=>import(/*webpackChunkName:"about"*/'../views/About.vue')
}
]
// main.js
new Vue({
router,
render:h=>h(App)
}).$mount('#app')
<router-view />
<router-link to='/'>首页</router-link>
<router-link to='/about'>关于我们</router-link>
// router.js
{
path:'/',
component:Home,
children:[{
path:'/list',
name:'list',
component:List
}]
}
// Home.vue
<template>
<div class='home'>
<h1>首页</h1>
<router-view />
</div>
</template>
// params传参
// 路由配置
{path:'detail/:id',name:'detail',component:Detail}
// 路由传参
this.$router.push({name:'detail',params:{id:101}})
// 接收路由参数
console.log(this.$route.params.id)
// query传参
this.$router.push({
path:'/detail',
query:{
id:101
}
})
// 接受参数
console.log(this.$router.query.id)
// router-link 传参
<router-link :to="{path:'/detail',query:{id:101}}"></router-link>
<router-link :to="{name:'detail',params:{id:101}}"></router-link>
// 全局路由守卫 router.js
router.beforeEach((to,from,next){
// 需要访问about页面,如果未登录需要先去登录
if(to.meta.auth && !window.isLogin){
window.isLogin=true
// 登陆成功,继续访问页面
next()
}else{
// 不需要登录,直接访问页面
next()
}
})
// 路由独享守卫 写在路由配置里面
{
path:'/about',
component:About,
beforeEnter(to,from,next){
if(!window.login){
// 去登陆
}esle{
next()
}
}
}
// 组件内的路由守卫 写在组件里面
export default{
beforeRouterEnter(to,from,next){
// this不能用
},
beforeRouterUpdate(to,from,next){...},
beforeRouterLeave(to,from,next){...}
}
// 异步获取路由
// 后台存的方式:{path:'/',component:'Home',children:[{path:'/detail',component:'Detail'}]}
api.getRoutes().then(routes=>{
const routeConfig = routes.map(route => mapComponent(route));
router.addRoutes(routerConfig)
})
// 路由映射
const compMap={
'Home':()=>import('../views/Home.vue')
}
// 递归替换
function mapComponent(route){
route.component=compMap[route.component]
if(route.children){
route.children=route.children.map(chils=>mapComponent(child))
}
return route
}
vuex
使用export default new Vuex.Store({
state:{count:0},
mutations:{
increment(state,n=1){
state.count += 1
}
}
})
<div>冲啊,手榴弹扔了{{$store.state.count}}个<div>
<button @click='add'>再扔一个</button>
...
methods:{
add(){
this.$store.commit('increment')
}
}
getters:{
score(state){
return `一共扔了${state.count}个`
}
}
<span>{{$store.getters.score}}</span>
actions:{
incrementAsync({commit}){
setTimeout(()=>{// 模拟异步操作
commit('increment',2)
},1000)
}
}
<button @click='addAsync'>蓄力扔两个</button>
...
methods:{
addAsync(){
this.$store.dispatch('incrementAsync')
}
}
const count ={
namespaced:true,
state:{...},
getters:{...},
mititions:{...},
actions:{...}
}
export default new Vuex.Store({
modules:{a:count}
})
<div>冲啊,手榴弹扔了{{$store.state.a.count}}个</div>
<p>{{$store.gettersp['a/score']}}</p>
<button @click='add'>扔一个</button>
<button @click='addAsync'>蓄力扔两</button>
...
methods:{
add(){
this.$store.commit('a/increment')
},
addAsync(){
this.$store.dispatch('a/incrementAsync')
}
}
<div>冲啊,手榴弹扔了{{count}}个</div>
<p>{{score}}</p>
<button @click='add'>扔一个</button>
<button @click='addAsync'>蓄力扔两</button>
...
computed: {
...mapState('a', {
count: state => state.count
}),
...mapGetters('a', {
score: 'score'
})
},
methods: {
...mapMutations('a', ['increment']),
...mapActions('a', ['incrementAsync']),
add () {
this.increment()
},
addAsync () {
this.incrementAsync()
}
}
Vue
插件vue.use
接收一个对象或者方法
vue.imxin
vue.use
使用,扩展vue
实例vue
实例混入data、生命周期、methods等vuex
// myPlugins/index.js
module.exports={
install:function(Vue){
Vue.mixin({
created(){
if(this.$options.isVuex){
// import不能接收变量,只能接收字符串
import('../store/modules/'+this.$options.name).then(res=>{
this.$store.registerModule(this.$options.name,res.default)
})
}
}
})
}
}
// 使用 mian.js
import plugins from './myPlugins'
Vue.use(plugins)
Vue
按钮级别权限控制v-premission
,从而实现按钮级别权限控制import store from '@/store'
const permission={
inserted(el,binding){
// 获取指令的值:按钮要求的角色数组
const {value:pRoles}=binding
// 获取用户角色
const roles=store.getters && store.getters.roles
if(pRoles && pRoles instanceof Array && pRoles.length>0){
// 判断用户角色中是否有按钮要求的角色
const hasPermission=roles.some(rols=>{
return pRoles.includes(role)
})
// 如果没有权限删除当前DOM
if(!hasPermission){
el.parentNode && el.parentNode.removeChild(el)
}
}else{
throw new Error(`请指定按钮要求的角色权限,如v-permission="['admin','editor']"`)
}
}
}
export default permission
import vPermission from './directive/permission'
Vue.directive('permission',vPermission)
// 给按钮添加权限
<button v-permission="['admin','editor']">编辑</button>
<button v-permission="['admin']">管理</button>
vue-cli 3.0
项目自定义webpack
配置vue.config.js
// vue.config.js
const port=3000
const title='webpack'
const path=require('path')
function resolve(dir){
return path.join(__dirname,dir)
}
module.exports={
publicPath:'',// 部署应用时的基本URL
devServer:{
port:port
},
configureWebpack:{
name:title, // index.html 使用:<%= webpackConfig.name %>
plugins:[
new MyWebpackPlugin()
]
},
chainWebpack(config){
// 配置svg规则排除icons目录中svg文件处理
config.module
.rule('svg')
.exclude.add(resolve('src/icons'))
.end()
// 新增icons规则,设置svg-sprite-loader处理icons目录中的svg
config.module
.rule('icons')
.test(/\.svg$/)
.include.add(resolve('src/icons'))
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({symbolId:'icon-[name]'})
.end()
}
}
svg
图标自动导入// icons/index.js
const req=require.context('./svg',false,/\.svg$/)
req.keys().map(req)
// main.js
import './icons'
SvgIcon
组件// components/SvgIcon.vue
<template>
<svg :class='svgClass' aria-hidden='true' v-on='$listeners'>
<use :xlink:href='iconName' />
</svg>
</template>
<script>
export default {
name:'SvgIcon',
props:{
iconClass:{
type:String,
required:true
},
className:{
type:String,
default:''
}
},
computed:{
iconName(){
return `#icon-${this.iconClass}`
},
svgClass(){
if(this.className){
return 'svg-icon'+this.className
}else{
return 'svg-icon'
}
}
}
}
</script>
<style>
.svg-icon{
width:1em;
height:1em;
vertical-align:-0.15em;
fill:currentColor;
overflow:hidden;
}
</style>
SvgIcon
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon.vue'
Vue.component('svg-icon',SvgIcon)
SvgIcon
<svg-icon icon-class='qq'></svg-icon>
svg
图标,放到src/icons/svg
下面,就可以在组件中使用了