vue底层实现原理
Vue是一个MVVM框架,M->model(数据模型)指的是后端传递的数据,V->view(视图、模板)指的是看到的页面,VM->ViewModel(视图模型)是连接view和model的桥梁。对比MVC(Model-View- Controller),即模型-视图-控制器,Controller指的是页面业务逻辑,M和V的代码是分离的,通过Controller承上启下进行单项通信。VM并没有替代Controller,而是在C的基础上增加了一层VM,弱化了C的概念。
vue的双向绑定
vue的双向绑定通过v-model实现,展现出的效果就是数据改变视图更新,就是将模型(Model)转化成视图(View),视图改变数据更新,就是将视图(View)转化成模型(Model)。其原理是采用数据劫持结合发布者-订阅者模式,在数据变动时发布消息给订阅者,触发相应的监听回调。
Observer监听数据对象的所有属性,Obeject.defineProperty()函数可以劫持各个属性的setter和getter,当初次绑定时就会触发getter,而数据发生变化时就会触发setter,setter或getter被触发后Observer就要通知Watcher订阅者。
Watcher订阅者是Observer和Compile之间通信的桥梁,它会在自身实例化时往属性订阅器(dep)里面添加自己,当属性变动dep.notice()通知时,通过调用自身的update()方法触发Compile中绑定的回调。
Compile则可以解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的Watcher订阅者,一旦数据有变动,收到通知,更新视图。
vue快速搭建
1、 下载node.js (http://nodejs.cn/) 并检查是否安装成功
node -v
npm -v
2、安装vue脚手架并创建一个基于 webpack 模板的项目
npm install -g vue-cli
vue --version //查看脚手架版本
vue init webpack 项目名
cd 项目名
npm install
npm run dev
注:创建期间会要求选择安装依赖时要用的包管理器,两个选项npm和yarn,区别在于yarn速度更快、安装版本统一、输出更简洁
vue项目目录简介
1、build:构建脚本目录
- build.js ==> 生产环境构建脚本;
- check-versions.js ==> 检查npm,node.js版本;
- utils.js ==> 构建相关工具方法;
- vue-loader.conf.js ==> 配置了css加载器以及编译css之后自动添加前缀;
- webpack.base.conf.js ==> webpack基本配置;
- webpack.dev.conf.js ==> webpack开发环境配置;
- webpack.prod.conf.js ==> webpack生产环境配置;
2、config:项目配置
- dev.env.js ==> 开发环境变量;
- index.js ==> 项目配置文件;
- prod.env.js ==> 生产环境变量;
3、node_modules:npm 加载的项目依赖模块
4、src:项目具体开发
- assets:资源目录,放置一些图片或者公共js、公共css。这里的资源会被webpack构建;
- components:组件目录,存放公共组件;
- pages: 自己新建的组件目录,存放主要页面的组件
- router:前端路由,需要配置的路由路径写在index.js里面;
- App.vue:根组件;
- main.js:入口js文件;
5、static:静态资源目录,如图片、字体等。不会被webpack构建
6、index.html:首页入口文件,可以添加一些 meta 信息等
7、package.json:npm包配置文件,定义了项目的npm脚本,依赖包等信息
8、README.md:项目的说明文档,markdown 格式
vue组件传值
1、 父组件跟子组件通信
通过在parent中的子组件标签中绑定message,然后在子组件中采用props接收父组件传过来的数据。
**father.vue**
<template>
<child :message='message'></child>
</template>
<script>
import child from 'child.vue';
export default {
name: "father",
data() {
return {
message: 'hello'
}
},
components: {
child
}
}
</script>
**child.vue**
<template>
<div class='child'>{{ message }}</div>
</template>
<script>
export default {
name: "child",
props: ['message']
}
</script>
2、子组件跟父组件通信
子组件通过this.$emit()把自定义事件沿着作用域链向上派送,父组件中的子组件标签绑定到这个自定义事件,并在事件发生时作出回应。
**father.vue**
<template>
// 父组件监听change事件,执行getChildData方法,并拿到传递过来的数据
<child @change='getChildData'></child>
</template>
<script>
import child from 'child.vue';
export default {
name: "father",
methods: {
getChildData (val) {
console.log(val); // 子组件传递过来的数据
}
},
components: {
child
}
}
</script>
**child.vue**
<template>
<div class='child'></div>
</template>
<script>
export default {
name: "child",
created () {
// 在需要的传递数据时调用setData方法
this.setData();
},
methods: {
setData(){
this.$emit('change', 'hello');
}
}
}
</script>
3、兄弟组件之间的通信(a传给b)
通过事件车Bus作为媒介,有点类似于子传父
**bus.js**
import Vue from 'vue';
export default new Vue;
a中触发 Bus.$emit 暴露自定义事件change
<template>
<div class='a'></div>
</template>
<script>
import Bus from 'bus.js' ;
export default {
name: "a",
created() {
// 在需要的传递数据时调用setData方法
this.setData();
},
methods: {
setData() {
Bus.$emit('change', 'hello');
}
}
}
</script>
b中通过Bus.$on监听change事件
<template>
<div class='b'></div>
</template>
<script>
import Bus from 'bus.js';
export default {
name: "b",
monted() {
Bus.$on('change', this.getData);
},
methods: {
getData (val) {
console.log(val); // a组件传递过来的数据
}
}
}
</script>
vuex状态管理
vuex
state ==> 基本数据,定义我们所需要管理的数组、对象、字符串等。
getters ==> 获取计算后的state 属性的值,类似vue.js的计算属性,state中的某个属性的值发生改变时getter的返回值才会被重新计算。
mutations ==> 更改 Vuex 的 store 中的状态(state),是同步的。
actions ==> 在组件中使用 this.$store.dispatch(‘xxx’) 分发 action,使mutations可以异步。
modules ==> 可以将 store 分割成模块(module),每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块,解决state中过于复杂臃肿的情况。
计算属性
路由
1、安装
npm install vue-router
2、使用
在src/router/index.js中引入vue、vue-router和所需组件,全局使用Router,配置链接路径、路由名称和对应的组件模板
import Vue from 'vue'
import Router from 'vue-router'
import Hello from '@/components/home'
Vue.use(Router) //Vue全局使用Router
export default new Router({
routes: [ //配置路由
{
path: '/', //链接路径
name: 'home', //路由名称,
component: home//对应的组件模板
},{
path:'/list',
component:list,
children:[ //子路由,嵌套路由
{path:'/',component:list},
{path:'listChild1',component:listChild1},
{path:'listChild2',component:listChild2},
]
}
]
})
3、vue-router中重定向redirect可使不同的路径都调回定向的页面
4、router-link组件,会被默认渲染成一个带有链接的a标签,通过to属性指定链接地址。它有一个tag属性,可以改变默认元素;还有一个active-class属性,被点击时使用。
<router-link to="/"></router-link> //渲染成a标签
<router-link to="/" tag="div"></router-link> //渲染成div标签
5、$route和 $router的区别
- router为VueRouter的实例,是一个全局路由对象,包含了路由跳转的方法、钩子函数等。
- route 是跳转的路由对象,每一个路由都会有一个route对象,是一个局部对象,包含path,params,hash,query,fullPath,matched,name等路由信息参数。
6、vue-router 传参
- 通过动态路由方式
在router.js文件中配置path的地方动态传递参数 path:’/detail/:id’ 然后在组件内通过this.$route.params.id即可获取
<router-link :to="'/detail/'+id">跳转</router-link>
//路由配置
{
path:'/detail/:id',
name:'Detail',
component:DetailComponent
}
- 通过params属性传值
参数不会显示在路径上,浏览器强制刷新参数会被清空,使用name
<route-link :to={path:'/index',params:{pid:id}}></route-link> //写入
this.pid = this.$route.params.pid; //读取
this.$router.push({ name: 'news', params: { userId: 123 }}) //写入
this.userId = this.$route.params.userId; //读取
- 通过query属性传值
参数会显示在路径上,刷新不会被清空,使用path路径
<route-link :to={path:'/index',query:{pid:id}}></route-link> //写入
this.userId = this.$route.query.userId; //读取
this.$router.push({ path: '/index', query: { userId: 123 }}) //写入
this.userId = this.$route.query.userId; //读取
导航守卫
在项目开发中有时候路由的切换或者页面的刷新需要判断用户是否已经登录,此时就需要用到导航守卫
1、全局守卫(全局钩子)
全局前置守卫 ==> router.beforeEach((to,from,next)=>{})
to:即将要进入的路由对象
from:正要离开的路由对象
next:函数,决定是否展示所要看到的路由页面。
- next()当前导航继续,使导航守卫继续向下迭代。
- next(false): 中断当前的导航。
- next(’/’) 或者 next({ path: ‘/’ }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。
router.beforeEach((to,from,next)=>{
if(to.path == '/login' || to.path == '/register'){
next();
}else{
alert('您还没有登录,请先登录');
next('/login');
}
})
全局解析守卫 ==>router.beforeResolve
全局后置守卫 ==>router.afterEach afterEach不在导航守卫队列中,所以没有next()
2、路由独享守卫 ==>beforeEnter
3、组件内的守卫 ==>beforeRouterEnter、beforeRouterUpdate、beforeRouterLeave
执行顺序:beforeRouterEnter>beforeEach>beforeRouterUpdate>beforeEnter>beforeRouterEnter>beforeResolve>afterEach