MVVM是Model-View-ViewModel的缩写,Model表明数据模型负责业务逻辑和数据封装,View表明UI组件负责界面和显示,ViewModel监听模型数据的改变和控制视图行为,处理用户交互,简单来讲就是经过双向数据绑定把View层和Model层链接起来。在MVVM架构下,View和Model没有直接联系,而是经过ViewModel进行交互,咱们只关注业务逻辑,不须要手动操做DOM,不须要关注View和Model的同步工做。javascript
vue核心是一个响应的数据绑定系统,mvvm,数据驱动,组件化,轻量,简洁,高效,快速,模块友好。html
缺点:不支持低版本浏览器,最低到IE9,不利于SEO的优化,首页加载时间较长,不可使用浏览器的导航按钮须要自行实现前进后退。vue
RESTful是一个api的标准,无状态请求。请求的路由地址是固定的。 restful:给用户一个url,根据method不一样在后端作不一样处理:好比post 建立数据,get 获取数据,put和patch修改数据,delete删除数据java
1.vue-cli名字改成@vue/cli,因此全局安装了旧版的要经过npm install vue-cli -g
卸载。vue-router
安装新版vue-clivuex
npm install -g @vue/cli
vue-cli
2.建立一个项目 vue create hello-world
3.assets文件夹是放静态资源;components是放组件;router是定义路由相关的配置;view视图;app.vue是一个应用主组件;main.js是入口文件npm
<input v-model="msg" />
复制代码
至关于编程
<input v-bind:value="msg" v-on:input="msg=$event.target.value" />
复制代码
v-show只是在display: none和display: block之间切换,只须要切换CSS,DOM仍是一直保留着,v-show在初始渲染时有更高的开销,可是切换开销很小,更适合频繁切换的场景后端
v-if涉及到vue底层的编译,当属性初始为false时组件不会被渲染,直到条件为true,而且切换条件时会触发销毁/挂载组件,切换时开销更高,更适合不常常切换的场景
route是路由信息对象,包括path,params,hash,query,fullPath,matched,name等路由信息参数。
router是路由实例对象,包括了路由的跳转方法,钩子函数。
在router目录下的index.js文件,对path属性加上:id,
使用router对象的params.id获取
active-class是vue-router模块中router-link组件的属性
使用children定义嵌套路由
keep-alive是一个内置组件,可以使被包含的组件保留状态或避免从新渲染,有include(包含的组件缓存)和exclude(排除的组件不缓存)两个属性。
.prevent: 提交时间再也不重载页面
.stop:阻止单击事件冒泡
.self:当事件发生在该元素自己而不是子元素的时候触发
.capture:事件侦听,事件发生的时候会调用
组件复用时全部组件实例都会共享data,若是data是对象就会形成一个组件修改data之后会影响到其余全部组件,因此须要将data写成函数,每次用到就调用一次函数得到新的数据
当咱们使用new Vue()的方式的时候,不管咱们将data设置为对象仍是函数都是能够的,由于new Vue()的方式是生成一个根组件,该组件不会复用,也就不存在共享data的状况
1.建立局部指令
directives:{
// 指令名称
dir1: {
inserted(el){
// 第一个参数是当前使用指令的DOM
el.style.width = '200px';
el.style.height = '200px';
el.style.background = '#000'
}
}
}
复制代码
2.全局指令
Vue.directive('dir2', {
inserted(el){
console.log(el)
}
})
复制代码
3.指令的使用
<div v-dir1></div>
<div v-dir2></div>
复制代码
<input type="text" v-model="msg" />
{{msg | capitalize}}
data(){
return{
msg: ''
}
},
filters: {
capitalize: function(value){
if(!value) return "";
value = value.toString();
return value.charAt(0).toUpperCase()+value.slice(1)
}
}
复制代码
computed是计算属性,依赖其余属性计算值,而且computed的值有缓存,只有当计算值变化才会返回内容
watch监听到值的变化就会执行回调,在回调中能够进行一些逻辑操做。
通常来讲须要依赖别的属性来动态得到值的时候可使用computed,对于监听到值的变化须要作一些复杂业务逻辑的状况可使用watch
另外computed和watch还支持对象的写法
data: {
firstName: 'Chen',
lastName: 'Miao',
fullName: 'Chen Miao'
},
watch: {
firstName: function(val){
this.fullName = val+ ' '+this.lastName
},
lastName: function(val){
this.fullName = this.firstName+ ' '+val
}
},
computed: {
anoFullName: function(){
return this.firstName+' '+this.lastName
}
}
复制代码
做用是扩展组件生成一个构造器,一般与$mount一块儿使用。
// 建立组件构造器
let Component = Vue.extend({
template: '<div>test</div>'
})
// 挂载到#app上
new Component().$mount('#app')
// 扩展已有组件
let SuperComponent = Vue.extend(Component)
new SuperComponent({
created(){
console.log(1)
}
})
new SuperComponent().$mount('#app')
复制代码
mixin用于全局混入,会影响到每一个组件实例,一般插件都是这样作初始化的。
Vue.mixin({
beforeCreate(){
// 会影响到每一个组件的beforeCreate钩子函数
}
})
复制代码
mixins最经常使用的扩展组件的方式。若是多个组件有相同的业务逻辑,就可将这些逻辑剥离出来,经过mixins混入代码。须要注意:mixins混入的钩子函数会先于组件内的钩子函数执行,而且在遇到同名选项的时候也会有选择性的进行合并。
nextTick可使咱们在下次DOM更新循环结束以后执行延迟回调,用于得到更新后的DOM
data:function(){
return {
message: '没有更新'
}
},
methods: {
updateMessage: function(){
this.message='更新完成'
console.log(this.$el.textContent) // '没有更新'
this.$nextTick(function(){
console.log(this.$el.textContent)// '更新完成'
})
}
}
复制代码
<transition name="fade1">
<router-view></router-view>
</transition>
复制代码
类名介绍:
父组件经过props传递数据给子组件,子组件经过emit发送事件传递给父组件。
// 父组件
<div>
<child :data="child" @send="getFromChild"></child>
</div>
data(){
return{
toChild: '大儿子',
fromChild: ''
}
},
methods: {
getFromChild(val){
this.fromChild=val
}
}
// 子组件
<div @click="toParent">{{data}}</div>
props:[data],
methods: {
toParent(){
this.$emit('send', '给父亲')
}
}
复制代码
v-model实际上是props,emit的语法糖,v-model默认会解析成名为value的prop和名为input的事件。
// 父组件
<children v-model="msg"></children>
<p>{{msg}}</p>
data(){
return{
msg:'model'
}
}
// 子组件
<input :value="value" @input="toInput" />
props: ['value'],
methods: {
toInput(e){
this.$emit('input', e.target.value)
}
}
复制代码
$children
访问子组件,在子组件中使用$parent
访问父组件// 父组件
<child />
data(){
return {
msg: '父组件数据'
}
},
methods: {
test(){
console.log('我是父组件的方法,被执行')
}
},
mounted(){
console.log(this.$children[0].child_msg); // 执行子组件方法
}
// 子组件
<div>{{$parent.msg}}</div>
data(){
return{
child_msg: '子组件数据'
}
},
mounted(){
// 子组件执行父组件方法
this.$parent.test();
}
复制代码
$listeners
和$attrs
$attrs
--继承全部父组件属性(除了prop传递的属性)
inheritAttrs--默认值true,继承全部父组件属性(除props),为true会将attrs中的属性当作html的data属性渲染到dom根节点上
$listeners
--属性,包含了做用在这个组件上全部监听器,v-on="$listeners"将全部事件监听器指向这个组件的某个特定子元素
// 父组件
<children :child1="child1" :child2="child2" @test1="onTest1"
@test2="onTest2"></children>
data(){
return {
child1: 'childOne',
child2: 'childTwo'
}
},
methods: {
onTest1(){
console.log('test1 running')
},
onTest2(){
console.log('test2 running')
}
}
// 子组件
<p>{{child1}}</p>
<child v-bind="$attrs" v-on="$listeners"></child>
props: ['child1'],
mounted(){
this.$emit('test1')
}
// 孙组件
<p>{{child2}</p>
<p>{{$attrs}}</p>
props: ['child2'],
mounted(){
this.$emit('test2')
}
复制代码
在vue1.x中是对prop进行双向绑定,在vue2只容许单向数据流,也是一个语法糖
// 父组件
<child :count.sync="num" />
data(){
return {
num: 0
}
}
// 子组件
<div @click="handleAdd">add</div>
data(){
return {
counter: this.count
}
},
props: ["count"],
methods: {
handleAdd(){
this.$emit('update:count', ++this.counter)
}
}
复制代码
能够经过查找父组件中的子组件实现, this.$parent.$children
在$children
中能够经过组件name查询到须要的组件实例,而后进行通讯
可使用provide/inject,虽然文档中不推荐直接使用在业务中。
假设有父组件A,而后有一个跨多层次的子组件B
// 父组件A
export default{
provide: {
data: 1
}
}
// 子组件B
export default{
inject: ['data'],
mounted(){
// 不管跨几层都能获取父组件的data属性
console.log(this.data); // 1
}
}
复制代码
能够用Vuex或Event Bus解决
1.新建一个bus.js文件
import Vue from 'vue';
export default new Vue();
复制代码
2.使用它
<div @click="addCart">添加</div>
import Bus from 'bus.js';
export default{
methods: {
addCart(event){
Bus.$emit('getTarget', event.target)
}
}
}
// 另外一组件
export default{
created(){
Bus.$on('getTarget', target =>{
console.log(target)
})
}
}
复制代码
单页面应用SPA的核心之一是:更新视图而不从新请求页面
router.push('home')
router.push({path: 'home')
const router=new VueRouter({
routes: [{
path: '/user',
name: 'user',
component: User
}]
})
复制代码
<router-link :to="{name: 'user'}"></router-link>
复制代码
router.push({
name: 'user'
})
复制代码
<div>{{$route.params.id}}</div>
复制代码
const router = new VueRouter({
routes: [{
path: '/user/:id',
component: User
}]
})
复制代码
router.push({name:'user',params: {id: 123})
<h2>{{$route.params.id}}</h2>
<router-view></router-view>
复制代码
const router = new VueRouter({
routes: [{
path: '/user/:id',
children: [{
// 当/user/:id
path: '',
component: UserHome
},{
// 当/user/:id/profile
path: 'profile',
component: UserProfile
}]
}]
})
复制代码
router.push({path:'register',query:{plan:'private'})
参数:
1.字符串
router.push('home')
2.对象
router.push({path: 'home'})
3.命名的路由
router.push({ name: 'user', params: { userId: 123 } })
4.带查询参数,变成/register?plan=private
router.push({ path: 'register', query: { plan: 'private' } })
不会向history添加新纪录,替换当前的history记录
点击<router-link :to="..." replace>
等同于调用router.replace(...)
在历史记录中向前或向后退多少步
// 前进一步,等同history.forward()
router.go(1)
// 后退一步
router.go(-1)
// 前进3步记录
router.go(3)
复制代码
<router-view></router-view>
<router-view name="a"></router-view>
<router-view name="b"></router-view>
复制代码
const router = new VueRouter({
routes: [{
path: '/',
components: {
default: Foo,
a: Bar,
b: Baz
}
}]
})
复制代码
导航钩子主要用来拦截导航,让它完成跳转或取消。
router.beforEach((to,from,next) => {})
钩子是异步执行解析的,每一个钩子方法接收三个参数:
to: Route即将进入的目标路由对象
from: Route当前导航正要离开的路由
next: Function,调用该方法来resolve这个钩子,执行效果看参数
next():进行下一个钩子
next(false):中断当前的导航
next('/')或next({path: '/'}):跳转到另外一地址
生命周期共分为8个阶段建立前/后,载入前/后,更新前/后,销毁前/后
建立前/后:在beforeCreated阶段,vue实例的挂载元素el和数据对象data都为undefined,还未初始化。created阶段,vue实例的数据对象data有了,el尚未。
载入先后:在beforeMount阶段,vue实例的el和data都初始化了,但仍是挂载以前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染
更新前/后:当data变化时,会触发beforeUpdated和updated方法
销毁前/后:beforeDestroy在实例销毁前调用,实例仍然彻底可用。destroy在实例销毁以后调用,调用后全部事件监听器会被移除,全部子实例也会被销毁。
生命周期中有多个事件钩子,让咱们在控制整个Vue实例的过程当中更容易造成好的逻辑。
1.vuex是vue生态系统中的状态管理,用来管理vue中的全部组件状态。
2.使用
import Vue from vue;
import Vuex from vuex;
Vue.use(Vuex);
const store = new Vuex.store({
state: {
count: 0
},
getters: {
addTen: state => {
return state.count+10;
}
},
mutations: {
increment(state){
state.count++
}
}
})
复制代码
store.commit('increment');
console.log(this.$store.state.count);
复制代码
vuex中有
①state状态,还有mapState映射状态
computed: mapState({
count: state => state.count
})
复制代码
getter至关于store的计算属性,主要用来过滤一些数据
getters: {
addTen: state => {
return state.count+10;
}
}
store.getters.addTen
复制代码
mapGetters是将store中的getter映射到局部计算属性中
computed: {
...mapGetters([
'addTen'
])
}
复制代码
③Mutation 改变vuex中store状态惟一方法就是提交mutation,可传入额外参数,是一个同步函数。 在组件中提交Mutation
import {mapMutations} from 'vuex'
export default{
methods: {
...mapMutations([
'increment'
]),
...mapMutaions({
add: 'increment'
})
}
}
复制代码
④Action 相似mutation,可是是异步的,view层经过store.dispath分发action
actions:{
increment(context){
context.commit('increment')
}
}
复制代码
⑤module 当应用比较复杂,能够将store分割成模块
3.经常使用的场景有:单页应用中,组件之间的状态,音乐播放、登陆状态、加入购物车等等
vue.js是采用数据劫持结合发布者-订阅者模式的方式,经过 Object.definePorperty() 来劫持各个属性的setter,getter,在数据变更时发布消息给订阅者,触发相应的监听回调。
MVVM做为数据绑定的入口,整合Observer,Compile和Watcher三者,经过Observer来监听本身的model数据变化,经过Compile来解析编译模板指定(解析{{}}),最终利用Watcher搭起Observer和Compile之间的通讯桥梁,达到数据变化->视图更新;视图交互变化input->数据model变动的双向绑定效果
实现简单的双向绑定
<input type="text" id="inp" />
<div id="show"></div>
<script type="text/javascript">
var inp = document.getElementById('inp');
var show = document.getElementById('show');
var obj = {};
function watch(obj, key, callback){
var val = obj[key];
Object.defineProperty(obj, key, {
get: function(){
return val;
},
set: function(newVal){
callback(newVal, this)
}
})
}
watch(obj, "input", function(val){
show.innerHTML = val
})
inp.addEventListener('keyup', function(e){
obj.input = e.target.value
})
</script>
复制代码
Proxy在目标对象以前架设一层“拦截”,外界对该对象的访问都必须先经过这层拦截,所以提供一种机制,能够对外界的访问进行过滤和改写。
<input type="text" id="txt" />
<div id="show"></div>
<script type="text/javascript">
var inp = document.getElementById('txt');
var show = document.getElementById('show')
var obj = {}
var objKey = 'text'; // 将键保存起来
// Object.defineProperty
Object.defineProperty(obj, objKey, {
get: function(){
return obj[objKey];
},
set: function(newVal){
show.innerHTML = newVal
}
})
inp.addEventListener('keyup', function(e){
obj[objKey] = e.target.value
})
// proxy的实现
const newObj = new Proxy(obj, {
get: function(target, key, receiver){
return Reflect.get(target, key, receiver);
},
set: function(target, key, value,receiver){
if(key === objKey){
show.innerHTML = value
}
}
})
inp.addEventListener('keyup',function(e){
newObj[objKey] = e.target.value;
})
复制代码
Object.defineProperty的缺点:
1.不能检测到增长或删除的属性
2.数组方面的变更,如根据索引改变元素,以及直接改变数组长度时的变化,不能被检测到。
先转化成AST树,再获得render函数返回VNode(Vue的虚拟DOM节点)
详细步骤:首先经过compile编译器把template编译出AST语法树,而后AST会通过generate(将AST语法树转化成render function字符串的过程)获得render函数,render的返回值是VNode,VNode是Vue的虚拟DOM节点,里面有标签名,子节点,文本等