一、提到框架,先引入框架模式MVC、MVVM、MVP,下面分别介绍了其概念及异同
MVC是Model-View-Controller的缩写,特点在于实现关注点分离,即应用程序中的数据模型与业务和展示逻辑解耦。它将应用程序划分为三个部分:
Model: 模型(用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法)View: 视图(渲染页面)Controller: 控制器(M和V之间的连接器,用于控制应用程序的流程,及页面的业务逻辑)
MVC流程一共有两种:
一种是通过 View 接受指令,传递给 Controller,然后对模型进行修改或者查找底层数据,最后把改动渲染在视图上。 另一种是通过controller接受指令,传给View。
MVP是MVC改良模式,和MVC的相同之处在于:Controller/Presenter负责业务逻辑,Model管理数据,View负责显示只不过是将 Controller 改名为 Presenter,同时改变了通信方向。
M、V、P之间双向通信。View 与 Model 不通信,都通过 Presenter 传递。Presenter完全把Model和View进行了分离。
MVVM是Model-View-ViewModel的简写,主要目的是分离视图(View)和模型(Model)。
MVVM特点: 1) 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
2.)可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
3)独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。
4)可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过 Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会直接从Model中读取数据而不是通过 Controller。
MVVM 模式将 Presenter 改名为 ViewModel,基本上与 MVP 模式完全一致。 唯一的区别是,它采用双向绑定(data-binding):View的变动,自动反映在 ViewModel,反之亦然。这样开发者就不用处理接收事件和View更新的工作,框架已经帮你做好了。
二、Vue.js 是什么
Vue.js是一个轻巧、高性能、可组件化的MVVM库,同时拥有非常容易上手的API;
Vue.js是一个构建数据驱动的Web界面的库。
Vue.js是一套构建用户界面的渐进式框架。与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计。Vue 的核心库只关注视图层,并且非常容易学习,非常容易与其它库或已有项目整合。另一方面,Vue 完全有能力驱动采用单文件组件和 Vue 生态系统支持的库开发的复杂单页应用。数据驱动+组件化的前端开发。
简而言之:Vue.js是一个构建数据驱动的 web 界面的渐进式框架。Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。核心是一个响应的数据绑定系统。
什么是数据驱动
数据驱动是vue.js最大的特点。在vue.js中,所谓的数据驱动就是当数据发生变化的时候,用户界面发生相应的变化,开发者不需要手动的去修改dom。
vue专注于ViewModel层,view对应DOM,Model对应js对象。vue通过Directives指令操作view,对dom监听,通过dom listener改变model层变化。最终实现双向绑定。
那么vuejs是如何实现这种数据驱动的呢?(双向绑定)
首先,vuejs在实例化的过程中,会对实例化对象选项中的data 选项进行遍历,遍历其所有属性并使用 Object.defineProperty 把这些属性全部转为 getter/setter。同时每一个实例对象都有一个watcher实例对象,他会在模板编译的过程中,用getter去访问data的属性,watcher此时就会把用到的data属性记为依赖,这样就建立了视图与数据之间的联系。当之后我们渲染视图的数据依赖发生改变(即数据的setter被调用)的时候,watcher会对比前后两个的数值是否发生变化,然后确定是否通知视图进行重新渲染这样就实现了所谓的数据对于视图的驱动。
Vue.js的特性如下:
1.轻量级的框架
2.双向数据绑定
3.指令
4.插件化
渐进式的理解:
Vue的核心的功能,是一个视图模板引擎,但这不是说Vue就不能成为一个框架。如下图所示,这里包含了Vue的所有部件,在声明式渲染(视图模板引擎)的基础上,我们可以通过添加组件系统、客户端路由、大规模状态管理来构建一个完整的框架。更重要的是,这些功能相互独立,你可以在核心功能的基础上任意选用其他的部件,不一定要全部整合在一起。可以看到,所说的“渐进式”,其实就是Vue的使用方式,同时也体现了Vue的设计的理念
图挂了,见https://blog.csdn.net/weixin_41049850/article/details/79431682
比如,Angular,它两个版本都是强主张的,如果你用它,必须接受以下东西:
- 必须使用它的模块机制- 必须使用它的依赖注入
- 必须使用它的特殊形式定义组件(这一点每个视图框架都有,难以避免)
所以Angular是带有比较强的排它性的,如果你的应用不是从头开始,而是要不断考虑是否跟其他东西集成,这些主张会带来一些困扰。
比如React,它也有一定程度的主张,它的主张主要是函数式编程的理念,比如说,你需要知道什么是副作用,什么是纯函数,如何隔离副作用。它的侵入性看似没有Angular那么强,主要因为它是软性侵入。
Vue是渐进的,没有强主张,你可以在原有大系统的上面,把一两个组件改用它实现,当jQuery用。
三.Vue.js与其他框架的区别?
1.与angularjs的区别
相同点:
都支持指令:内置指令和自定义指令。都支持过滤器:内置过滤器和自定义过滤器。都支持双向数据绑定。都不支持低端浏览器。
不同点:
(1).AngularJS的学习成本高,比如增加了Dependency Injection特性,而Vue.js本身提供的API都比较简单、直观。(2).在性能上,AngularJS依赖对数据做脏检查,所以Watcher越多越慢。Vue.js使用基于依赖追踪的观察并且使用异步队列更新。所有的数据都是独立触发的。对于庞大的应用来说,这个优化差异还是比较明显的。
脏检查:
在 angular中,他没有办法判断你的数据是否做了更改, 所以它设置了一些条件,当你触发了这些条件之后,它就执行一个检测来遍历所有的数据,对比你更改了地方,然后执行变化。这个检查很不科学。而且效率不高,有很多多余的地方,所以官方称为 脏检查
2.与React的区别
相同点:
react采用特殊的JSX语法,Vue.js在组件开发中也推崇编写.vue特殊文件格式,对文件内容都有一些约定,两者都需要编译后使用。
中心思想相同:一切都是组件,组件实例之间可以嵌套。都提供合理的钩子函数,可以让开发者定制化地去处理需求。都不内置列数AJAX,Route等功能到核心包,而是以插件的方式加载。在组件开发中都支持mixins的特性。
不同点:
React依赖Virtual DOM,而Vue.js使用的是DOM模板。React采用的Virtual DOM会对渲染出来的结果做脏检查。Vue.js在模板中提供了指令,过滤器等,可以非常方便,快捷地操作DOM。
3.吸取两家之长,借鉴了Angular的指令(v-show,v-hide)和React的组件化(优点:可维护性,复用性)
四、vue核心内容:
1、vue-cli:
vue是以组件化开发的,最好要搭配webpack构建工具开发,独立搭建对大部分人较为困难,vue-cli就能很好解决这一问题。即使你对webpack还不是很了解,你也可以先搭建好项目在慢慢研究。cli已经将需要的东西配置好,只要写好项目业务,在用命令行就可以达到调试或打包的功能。
安装:
首先node版本最好高于4.x.x,稳定。然后执行npm install -g vue-cli,安装后选择webpack模板(也可选择其他模板)vue init webpack 项目名称然后按照提示选择一些选项,之后按照后面提示执行cd 项目名称,npm install即可。项目可通过npm run dev建立服务器,监听了8080端口,就可以通过localhost:8080访问了。
各文件功能:
项目名称
->build ------------------------------------------ webpack配置相关文件
->config ------------------------------------------ webpack配置相关文件
->node_modules ------------------------------------------ 依赖代码库
->src ------------------------------------------ 存放源码
->static ------------------------------------------ 存放第三方静态资源
->.babelrc.JSON ------------------------------------------ babe的配制,用于从es6编译至es5
->.editorconfig ------------------------------------------ 编译器配置
->.eslintignore ------------------------------------------ 忽略语法检查的目录文件
->.eslintrc.js ------------------------------------------ eslint的配置
->.gitignore ------------------------------------------ git忽略的目录或文件
->index.html ------------------------------------------ 入口文件,项目编译过程中会自动插入其中
->package.json ------------------------------------------ 配置文件,描述一个项目
->README.md ------------------------------------------ 项目描述文件
2.webpack
找到JS模块及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。
Webpack的工作方式是:把你的项目当做一个整体,通过一个给定的主文件(如:index.js),使用entry 属性来定义入口。Webpack将从这个文件开始找到你的项目的所有依赖文件,使用loaders处理它们,最后打包为一个或多个浏览器可识别的JavaScript文件。
出口:webpack 的 output 属性描述了如何处理归拢在一起的代码。
module.exports: 在我们自己写模块的时候,需要在模块最后写好模块接口,声明这个模块对外暴露什么内容,module.exports 提供了暴露接口的方法
插件在 webpack 的配置信息 plugins 中指定,用于完成一些 loader 不能完成的工作。比如我们可以安装内置的 BannerPlugin 插件,用于在文件头部输出一些注释信息
Babel是一个编译JSt的平台,能使用最新的JavaScript代码(ES6,ES7...),能使用基于JavaScript进行了拓展的语言,比如React的JSX。
Loader:webpack只能处理JS模块,如果要处理其他类型的文件,就需要使用 loader 进行转换。css-loader 和 style-loader,二者处理的任务不同, css-loader使你能够使用类似@import
和 url(...)
的方法实现 require()
的功能,style-loader将所有的计算后的样式加入页面中,二者组合在一起使你能够把样式表嵌入webpack打包后的JS文件中。
3.v-if和v-show
v-if是动态的向DOM树内添加或者删除DOM元素; 适合运营条件不大可能改变;切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;
v-show是通过设置DOM元素的display样式属性控制显隐;适合频繁切换;只是简单的基于css切换。
v-else元素必须跟在前面两者之一的后面,否则不能被识别。
4.v-bind
用于响应地更新 HTML 特性 形式如:v-bind:href 缩写为 :href;
5.v-on
用于监听DOM事件 形式如:v-on:click 缩写为 @click;
Vue.js 为v-on提供了事件修饰符。在事件处理程序中调用 event.preventDefault()
或 event.stopPropagation()
是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。
6.Vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式,也就是 Vuex 就是处理数据的。
store是Vuex应用的核心,基本上就是一个容器,它包含着你的应用中大部分的状态(state)。
Vuex如何管理数据:
state:唯一数据源 打个比方:state中定义一个x值,在整个应用中任意地方都可以取到。
getters: 就是计算属性。
mutations: 更改state的唯一方法(只能同步,异步处理放在action中)。每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler) ,事件类型 = 方法名,回调函数 = 方法
actions: 处理mutations不能做的异步操作。其中 分发 就是 触发
modules: 将前面四个分成modules = { state: { ... }, mutations: { ... }, actions: { ... }, getters: { ... } }
7、一些重要的全局API
Vue.extend():创建一个子类,参数是一个包含组件选项的对象。然后使用$mount挂载到元素上.
注:extend和component作用基本相同,区别仅在于当我们不需要组件名称时,使用extend更简便些; 而当我们需要使用新的标签时,那么请使用component去创建组件
var author = Vue.extend({
template: "<p><a :href='url'>{{author}}</a></p>",
data : function() {
return {
author : 'vamous',
url : 'http://blog.csdn.net/Dear_Mr/article/details/72614370'
}
}
});
对应的HTML:<author></author>
扩展实例构造器需要挂载:new author().$mount('author');
Vue.nextTick([callback,context]):修改数据后立刻使用这个方法,获取更新后的DOM
// 修改数据
vm.msg = 'Hello'
// DOM 还没有更新
Vue.nextTick(function () {
// DOM 更新了
})
Vue.set(object,key,value):设置对象的属性。如果对象是响应式的,确保属性被创建后也是响应式的,同时触发视图更新。这个方法用于避开Vue不能检测属性被添加的限制。
Vue.delete(object,key):删除对象的属性。如果对象是响应式的,确保删除能触发更新视图。这个方法主要用于避开 Vue 不能检测到属性被删除的限制,但是你应该很少会使用它。
Vue.directive(id,function(){}):注册指令,指令跟组件一样需要注册才能使用。提供了五个钩子函数来供我们使用,分别代表了一个组件的各个生命周期。
>bind: 只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。
>inserted: 被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中)。
>update: 被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新(详细的钩子函数参数见下)。
>componentUpdated: 被绑定元素所在模板完成一次更新周期时调用。
>unbind: 只调用一次, 指令与元素解绑时调用。
实例,刷新页面input自动获取焦点
<div id="app">
<input type="text" v-focus/>
</div>
<script>
// 注册一个全局自定义指令 v-focus
Vue.directive('focus', {
// 当绑定元素插入到 DOM 中。
inserted: function (el,binding) {
// 聚焦元素
el.focus();
}
});
new Vue({
el:'#app'
});
</script>
Vue.filter( id, [definition] ):注册或获取全局过滤器.
// 注册
Vue.filter('my-filter', function (value) {
// 返回处理后的值
})
// getter,返回已注册的过滤器
var myFilter = Vue.filter('my-filter')
Vue.component(id, [definition]):注册或获取全局组件。注册还会自动使用给定的id
设置组件的名称
// 注册组件,传入一个扩展过的构造器
Vue.component('my-component', Vue.extend({ /* ... */ }))
// 注册组件,传入一个选项对象(自动调用 Vue.extend)
Vue.component('my-component', { /* ... */ })
// 获取注册的组件(始终返回构造器)
var MyComponent = Vue.component('my-component')
Vue.mixin:混入 (mixins) 是一种分发 Vue 组件中可复用功能的非常灵活的方式。混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。
通俗一点的说,就是定义一部分公共的方法或者计算属性,然后混入到各个组件中使用,方便管理与统一修改。
// 定义一个混入对象
var myMixin = {
created: function () {
this.hello()
},
methods: {
hello: function () {
console.log('hello from mixin!')
}
}
}
// 定义一个使用混入对象的组件
var Component = Vue.extend({
mixins: [myMixin]
})
var component = new Component() // => "hello from mixin!"
8、props:可以是数组或对象,用于接收来自父组件的数据。
9、watch:键是需要观察的表达式,值是对应回调函数。Vue实例将会在实例化时调用之,遍历watch对象的每一个属性。
10、render:字符串模板的代替方案,允许你发挥 JavaScript 最大的编程能力。
11、生命周期钩子
beforecreated:el 和 data 并未初始化 ,可以在这加个loading事件
created:完成了 data 数据的初始化,el没有,在这结束loading,还做一些初始化,实现函数自执行
beforeMount:完成了 el 和 data 初始化
mounted:完成挂载, 在这发起后端请求,拿回数据,配合路由钩子做一些事情
beforeDestory:销毁,执行了destroy操作,后续就不再受vue控制了。应用:你确认删除XX吗? destroyed :当前组件已被删除,清空相关内容。
注意:不要在选项属性或回调上使用箭头函数,比如 created: () => console.log(this.a)
或 vm.$watch('a', newValue => this.myMethod())
。因为箭头函数是和父级上下文绑定在一起的,this
不会是如你所预期的 Vue 实例,经常导致 Uncaught TypeError: Cannot read property of undefined
或 Uncaught TypeError: this.myMethod is not a function
之类的错误。
12、model(2.2.0新增)
允许一个自定义组件在使用 v-model
时定制 prop 和 event。默认情况下,一个组件上的 v-model
会把 value
用作 prop 且把 input
用作 event,但是一些输入类型比如单选框和复选框按钮可能想使用 value
prop 来达到不同的目的。使用 model
选项可以回避这些情况产生的冲突。
Vue.component('my-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
// this allows using the `value` prop for a different purpose
value: String,
// use `checked` as the prop which take the place of `value`
checked: {
type: Number,
default: 0
}
},
// ...
})
<my-checkbox v-model="foo" value="some value"></my-checkbox>
13、组件
每用一次组件,就会有一个它的新实例被创建。
一个组件的 data
选项必须是一个函数。
可以调用内建的$emit方法,并传入事件的名字,来向父级组件触发一个事件。
全局注册和局部注册:
全局注册:
Vue.component('my-component-name', {
// ... 选项 ...
})
它们在注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中
Vue.component('component-a', { /* ... */ })
Vue.component('component-b', { /* ... */ })
Vue.component('component-c', { /* ... */ })
new Vue({ el: '#app' })
html
<div id="app">
<component-a></component-a>
<component-b></component-b>
<component-c></component-c>
</div>
全局注册的弊端在于:如果你使用webpack这样的构建系统,全局注册意味着即便你不再使用一个组件,它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript 的无谓的增加。
局部注册:
var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }
然后
new Vue({
el: '#app'
components: {
'component-a': ComponentA,
'component-b': ComponentB
}
})
注意局部注册的组件在其子组件中不可用。例如,如果你希望 ComponentA
在 ComponentB
中可用,则你需要这样写:
var ComponentA = { /* ... */ }
var ComponentB = {
components: {
'component-a': ComponentA
},
// ...
}
14、slot
概述:简单来说,假如父组件需要在子组件内放一些DOM,那么这些DOM是显示、不显示、在哪个地方显示、如何显示,就是slot分发负责的活。
默认情况下,父组件在子组件内套的内容是不显示的:
<div id="app">
<children>
<span>12345</span>
<!--上面这行不会显示-->
</children>
</div>
<script>
var vm = new Vue({
el: '#app',
components: {
children: {
template: "<button>为了明确作用范围,所以使用button标签</button>"
}
}
});
</script>
显示内容是一个button按钮,不包含span标签里面的内容;
使用slot标签的话,可以将父组件放在子组件的内容,放到想让他显示的地方。父组件放在子组件里的内容,插到了子组件的<slot></slot>位置;即使有多个标签,会一起被插入,相当于用父组件放在子组件里的标签,替换了<slot></slot>这个标签。
<div id="app">
<children>
<span>12345</span>
<!--上面这行不会显示-->
</children>
</div>
<script>
var vm = new Vue({
el: '#app',
components: {
children: { //这个无返回值,不会继续派发
template: "<button><slot></slot>为了明确作用范围,所以使用button标签</button>"
}
}
});
</script>
结果是:
<button><span>12345</span>为了明确作用范围,所以使用button标签</button>
假如父组件没有在子组件中放置有标签,或者是父组件在子组件中放置标签,但有slot属性,而子组件中没有该slot属性的标签。那么,子组件的slot标签,将不会起到任何作用。
15、provide和inject:
所有后代都需要访问同一个方法时,使用$parent无法很好扩展到更深层记得嵌套组件,可使用上述方法。例如:
<google-map>
<google-map-region v-bind:shape="cityBoundaries">
<google-map-markers v-bind:places="iceCreamShops"></google-map-markers>
</google-map-region>
</google-map>
所有的<google-map>后代都需要getMap方法,provide允许制定提供给后代组件的数据/方法:
provide: function () {
return {
getMap: this.getMap
}
}
然后再相应组件里使用inject接受属性。
inject: ['getMap']
16、过渡(transition)
整个阶段有6个class过程:
v-enter:进入过渡的开始状态,元素被插入前生效,被插入后的下一帧移除;
v-enter-active:进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数;
v-enter-to:进入过渡的结束状态。在元素被插入之后下一帧生效 ,在过渡/动画完成之后移除;
v-leave:离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除;
v-leave-active:离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。
v-leave-to:离开过渡的结束状态。在离开过渡被触发之后下一帧生效,在过渡/动画完成之后移除。
注:如果你使用一个没有名字的 <transition>
,则 v-
是这些类名的默认前缀。如果你使用了 <transition name="my-transition">
,那么 v-enter
会替换为 my-transition-enter
动画animationend,同过渡,区别是在动画中v-enter在节点插入DOM后不回立即删除,而在animationend触发时删除。
17、自定义指令:
使用directive创建,如下所示。
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
//局部指令
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
18、过滤器:
只能在双花括号内和v-bind表达式使用。定义通过filters:{}实现(或者全局的vue.filter())。