备战秋招,复习基础。若有错误,欢迎批评指正,共同进步!html
构建用户界面的渐进式框架前端
只关注图层,自底向上增量开发(增量是什么TBC!!!)vue
代码只须要关注逻辑层,DOM操做由VUE处理。node
经过尽量简单的API实现响应的数据绑定和组合的组件视图。ios
资料参考:Vue原理解析——本身写个Vuevue-router
资料参考:剖析Vue原理&实现双向绑定MVVMvuex
document.createDocumentFragment()
,方法建立虚拟dom树。vue的vnode代码部分位于项目的src/core/vdom文件夹下,vue的Virtual DOM是基于snabbdom修改的,vnode类的数据结构以下npm
export default class VNode {
tag: string | void;
data: VNodeData | void;
children: ?Array<VNode>;
text: string | void;
elm: Node | void;
ns: string | void;
context: Component | void; // rendered in this component’s scope
functionalContext: Component | void; // only for functional component root nodes
key: string | number | void;
componentOptions: VNodeComponentOptions | void;
componentInstance: Component | void; // component instance
parent: VNode | void; // component placeholder node
raw: boolean; // contains raw HTML? (server only)
isStatic: boolean; // hoisted static node
isRootInsert: boolean; // necessary for enter transition check
isComment: boolean; // empty comment placeholder?
isCloned: boolean; // is a cloned node?
isOnce: boolean; // is a v-once node?
asyncFactory: Function | void; // async component factory function
asyncMeta: Object | void;
isAsyncPlaceholder: boolean;
ssrContext: Object | void;
constructor (
tag?: string,
data?: VNodeData,
children?: ?Array<VNode>,
text?: string,
elm?: Node,
context?: Component,
componentOptions?: VNodeComponentOptions,
asyncFactory?: Function
) {
this.tag = tag
this.data = data
this.children = children
this.text = text
this.elm = elm
this.ns = undefined
this.context = context
this.functionalContext = undefined
this.key = data && data.key
this.componentOptions = componentOptions
this.componentInstance = undefined
this.parent = undefined
this.raw = false
this.isStatic = false
this.isRootInsert = true
this.isComment = false
this.isCloned = false
this.isOnce = false
this.asyncFactory = asyncFactory
this.asyncMeta = undefined
this.isAsyncPlaceholder = false
}
// DEPRECATED: alias for componentInstance for backwards compat.
/* istanbul ignore next */
get child (): Component | void {
return this.componentInstance
}
}
复制代码
Object.defineProperty()
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。 Object.defineProperty(obj, prop, descriptor)
json
将须要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter和getter 这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化。axios
对其更底层对象属性的修改或获取的阶段进行了拦截
var data = {name: 'kindeng'};
observe(data);
data.name = 'dmq'; // 哈哈哈,监听到值变化了 kindeng --> dmq
function observe(data) {
if (!data || typeof data !== 'object') {
return;
}
// 取出全部属性遍历
Object.keys(data).forEach(function(key) {
defineReactive(data, key, data[key]);
});
};
function defineReactive(data, key, val) {
observe(val); // 监听子属性
Object.defineProperty(data, key, {
enumerable: true, // 可枚举
configurable: false, // 不能再define
get: function() {
return val;
},
set: function(newVal) {
console.log('哈哈哈,监听到值变化了 ', val, ' --> ', newVal);
val = newVal;
}
});
}
复制代码
维护一个数组,用来收集订阅者,数据变更触发notify,再调用订阅者的update方法。
在数据变更时发布消息给订阅者,触发相应的监听回调。
// ... 省略
function defineReactive(data, key, val) {
var dep = new Dep();
observe(val); // 监听子属性
Object.defineProperty(data, key, {
// ... 省略
set: function(newVal) {
if (val === newVal) return;
console.log('哈哈哈,监听到值变化了 ', val, ' --> ', newVal);
val = newVal;
dep.notify(); // 通知全部订阅者
}
});
}
function Dep() {
this.subs = [];
}
Dep.prototype = {
addSub: function(sub) {
this.subs.push(sub);
},
notify: function() {
this.subs.forEach(function(sub) {
sub.update();
});
}
};
复制代码
compile主要作的事情是解析模板指令,将模板中的变量替换成数据,而后初始化渲染页面视图,并将每一个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变更,收到通知,更新视图
Watcher订阅者做为Observer和Compile之间通讯的桥梁,主要作的事情是:
MVVM做为数据绑定的入口,整合Observer、Compile和Watcher三者,经过Observer来监听本身的model数据变化,经过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通讯桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变动的双向绑定效果。
!!!太复杂了!!!还要再研究研究!!!QAQ
var vm = new Vue({
el:'至关于id',
data:{...},
template:{...},
methods:{...},函数调用,不能缓存 → {{methodTest()}}
computed:{...},属性调用,具备缓存功能,依赖于data中的数据,只有在它的相关依赖数据发生改变时才会从新求值 → {{computedTest}}
watch:{...},监测Vue实例上的数据变更
render:{...} 渲染优先级:render → template → outerHTML
})
data() {
return {
demo: {
name: ''
},
value: ''
};
},
computed: {
newName() {
return this.demo.name;
}
},
watch: {
newName(val) {
this.value = val;
}
}
复制代码
文本插值<p>{{message}}</p>
HTML插值<div v-html:"message></div>
属性v-bind:class="{'class1':use}"
→ 动态赋值
指令v-
→ 如:v-if
v-else
用户输入v-model
→ 实现数据双向绑定
事件监听 v-on:click
用处 | 范例 |
---|---|
访问根实例 | this.$root.foo |
访问父级组件实例 | this.$parent |
访问子组件实例(非响应式) | 子<input ref="input"> 父this.$refs.input.focus() |
依赖注入(访问任意上级) | 父 provide:funtion(){ return{getMap:this.getMap}} 子 inject:['getMap'] |
$:
${...}
只有当实例被建立时data中存在的属性才是响应式的!即:要先初始化,哪怕为空也行
实例方法
vm.$el === document.getElementById('example')
vm.$watch('a',function(new,old){在vm.a改变时调用}
复制代码
不要在选项属性或回调函数中使用箭头函数!
参考资料:手把手教Vue--生命周期
方法名 | 状态 | 含义 | 用法 |
---|---|---|---|
beforeCreate | creating 状态 | 实例建立以前调用 | 加 Loading 事件 |
created | creating 状态 | 实例建立成功,此时 data 中的数据显示出来了 | 页面没有加载完成就请求数据,结束 Loading |
beforeMount | mounting 状态 | 数据中的 data 在模版中先占一个位置 | |
mounted | mounting 状态 | 模版中的 data 数据直接显示出来了 | 发起异步服务端请求 |
beforeUpdate | updating 状态 | 当 data 数据发生变化调用,发生在虚拟 DOM 从新渲染和打补丁以前 | |
updated | updating 状态 | 数据更改致使的虚拟 DOM 从新渲染和打补丁 | |
beforeDestroy | destroying 状态 | 在 vue 实例销毁以前调用,此时实例任然可用 | 弹出确认删除 |
destroyed | destroying 状态 | 在 vue 实例销毁以后调用,vue实例指示的全部东西都会解绑定,全部的事件监听器会被移除,全部的子实例也会被销毁 | 关闭定时器,中止监听属性,清除相关数据达到资源的有效利用 |
HTML里
<div id = "app">
<router-link to="/foo">Go to Foo</router-link>
<router-view></router-view>
</div
复制代码
JS里
1 定义路由组件 const Foo = {template:'<div>foo</div>'}
2 定义路由 const routes = [{path:'/foo',component:Foo}]
3 建立router实例 const router = new VueRouter({routes})
4 建立挂载根实例 const app = new Vue({router}}).$mount('#app')
复制代码
属性 | 用法 |
---|---|
to | 目标路由的连接 |
replace | 点击时调用 |
append | 添加相对路径的基路径 :to="{path:'relative/path'}" append |
tag | 渲染成某种标签 如<li> |
active-class | 连接激活时使用的CSS类名 |
exact-active-class | 连接精确匹配时的CSS类名 |
event | 触发导航的事件,如event="mouseover →鼠标移动到上面时导航html内容改变 |
keep-alive
缓存失活的组件。切换页面,切换回来,页面状态不变
参考资料:vue-router的两种模式(hash和history)及区别
http://www.abc.com/#/hello
,hash的值为#/hello
.它的特色在于:hash虽然出如今URL中,但不会被包括在HTTP请求中,对后端彻底没有影响,所以改变hash不会从新加载页面。1 $router.push() //调用方法
2 HashHistory.push() //根据hash模式调用,设置hash并添加到浏览器历史记录(添加到栈顶)(window.location.hash= XXX)
3 History.transitionTo() //监测更新,更新则调用History.updateRoute()
4 History.updateRoute() //更新路由
5 {app._route= route} //替换当前app路由
6 vm.render() //更新视图
复制代码
pushState()
和replaceState()
方法。(须要特定浏览器支持)1.push
与hash模式相似,只是将window.hash改成history.pushState
2.replace
与hash模式相似,只是将window.replace改成history.replaceState
3.监听地址变化
在HTML5History的构造函数中监听popState(window.onpopstate)
复制代码
区别:
<transition name="动画名称" mode="out-in">
<div></div>
</transition>
复制代码
过渡模式:in-out / out-in
属性 | 用法 |
---|---|
v-enter | 进入过渡的开始状态 |
v-enter-active | 进入过渡生效时的状态,如{transition:opacity 2s} |
v-enter-to | 进入过渡的结束状态 |
v-leave | 离开过渡的开始状态 |
v-leave-active | 离开过渡生效时的状态 |
v-leave-to | 离开过渡的结束状态 |
mixins:可复用的方法或计算属性,可包含任意组件选项。
待补充!!!
经过XMLHttpRequest或JSONP发起请求并处理响应
可以使用全局对象方式vue.http
或在组件内部使用this.$http
来发期请求
get:function(){
this.$http.get('/someUrol').then(
function(res){
document.write(res.body);
},
function(){
console.log('error!')
}
);
}
复制代码
get(url,[options])
head(url,[options])
delete(url,[options])
jsonp(url,[options])
post(url,[body],[options])
put(url,[body],[options])
patch(url,[body],[options])
复制代码
基于promise的HTTP客户端
npm install axios
import axios from 'axios'
复制代码
get:
axios.get(url).then(function(response){...}).catch(function(error){...});
复制代码
post:
axios({
method:'post',
url:'/user',
data:{
firstName:'a',
lastName:'b'
}
});
复制代码
axios.all([func1(),func2()]).then{...}
→ 执行多个并发请求
axios.request(config)
axios.get(url[,config])
axios.delete(url[,config])
axios.head(url[,config])
axios.post(url[,data,[config]])
axios.put(url[,data,[config]])
axios.patch(url[,data,[config]])
复制代码
请求拦截
axios.interceptor.request.use(function(config){
//在发送请求前作的事
return config;
},function(error){
//请求错误时作的事
return Promise.reject(error);
});
复制代码
响应拦截
axios.interceptors.response.use(function(response){
//对响应数据作的事
return config;
},function(error){
//请求错误时作的事
return Promise.reject(error);
});
复制代码
required:true
default:100
validator:function(value){return ['success,'danger'].indexOf(value)!==-1
将子组件中<slot></slot>
替换为父组件<></>之间的任意代码
<slot>当父组件中不提供内容时的后备内容</slot>
具名插槽
父组件:<template v-slot:header></template>
子组件:<header></header>
复制代码
做用域插槽
父组件:<template v-slot:default="slotProps">
{{slotProps.user.firstName}}
</template>
子组件:<slot v-bind:user="user"></slot>
简写父组件:<current-user v-slot:"slotProps">slotProps.user.firstName</current-user>
再简写父组件:<current-user v-slot:"{user}">{{slotProps.user.firstName}}</current-user>
可定义后备值"{user={firstName:'Guest'}}"
复制代码
快速原型开发:对于单个 *.vue文件
vue serve
在开发环境模式下零配置为.js或.vue文件启动一个服务器
vue build
在生产环境模式下零配置构建一个.js或.vue文件
Preload 预加载首渲内容
Prefetch 预加载将来内容
载入
npm install vue-cookies
var Vue = require('vue')
Vue.use(require('vue-cookies')
或
import Vue from 'vue'
import VueCookies from 'vue-cookies'
Vue.use(VueCookies)
复制代码
用法 | 范例 |
---|---|
设置cookie | this.$cookies.set(keyName,value[,expireTime[,path[,domain[,secure]]]]) |
获取cookie | this.$cookies.get(keyName) // return value |
删除cookie | this.$cookies.remove(keyName[,path[,domain]]) |
存在? | this.$cookies.isKey(keyName) //retuen boolean |
获得所有 | this.$cookies.keys() |
状态管理模式 集中式存储管理应用的全部组件的状态
--- main.js ---
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
//配置...
});
new Vue({
el: '#app',
router:router,
store:store,
render:h=>{
return h(App)
}
});
复制代码
Vuex里的数据都是响应式的!!!
--- main.js ---
const store = new Vuex.Store({
state:{
count:0
}
});
--- index.vue ---
{{ $store.state.count}}
或
export default{
computed:{
count (){
return this.$store.state.count;
}
}
}
复制代码
辅助函数,获取多个状态,帮助生成计算属性
computed:{
localComputed(){
...
},
...mapState({
...
})
}
复制代码
计算store,返回值会缓存。依赖改变时会从新计算。可经过属性(缓存)和方法(不缓存)访问。
--- main.js ---
const store = new Vuex.Store({
state:{list:[1,5,8,10,30,50]},
getters:{
filteredList: state =>{
return state.list.filter(item => item <10); ← 以state为参数
},
listCount: (state,getters) =>{
return getters.filteredList.length; ← 以state和getters为参数
}
}
})
--- index.vue ---
export default{
computed:{
list (){
return this.$store.getters.filteredList;
},
listCount (){
return this.$store.getters.listCount;
}
}
}
复制代码
辅助函数,映射多个局部计算。
computed:{
...mapGetters([...也可另取名字])
}
复制代码
mutation
改变store中状态的惟一方法就是提交mutation(同步)
--- main.js ---
const store = new Vues.Store({
state:{list:[1,5,8,10,30,50]},
mutation:{
increment(state,params){
state.count+=params.count; ← 直接传入对象
},
descrease (state){
state.count --;
}
}
});
--- index.vue ---
export default{
methods:{
handleIncrement (){
this.$stroe.commit({
type:'increment',
count:10 ← 直接传入对象
});
},
handleDecrease (){
this.$store.commit('descrease');
}
}
}
复制代码
提交mutation(而非直接更改状态),支持异步
--- main.js ---
mutation:{
increment (state){
state.count++
}
},
actions:{
increment(与store实例具备相同方法和属性的context对象){
context.commit('increment')
}
异步
asyncIncrement (context){
return new Promise(resolve =>{
setTimeout(()=>{
context.commit('increment');
resolve();
},1000)
});
}
--- index.vue ---
methods:{
handleActionIncrement(){
this.$stroe.dispatch('increment');
},
handleAsyncIncrement (){
this.$store.dispatch('asyncIncrement').then(()=>{ ← 异步调用 返回的是Promise
console.log('...');
});
}
}
复制代码
辅助函数,映射多个dipatch调用
可嵌套:
actionB({dispatch,commit}){
return dispatch('actionA').then(()={
commit('other Mutation')
})
}
复制代码
可用async
/ await
组合action! → 一个dispatch触发多个action
涉及改变数据的,就用mutations
,存在业务逻辑的,就用actions
将store分割成模块
const moduleA = {
state:{...},
mutations:{...},
actions:{...}, ← 可接收rootState 同getter
getters:{
sumCount (state,getters,rootState){ ← 在模块内部调用根节点状态:rootState
return state.count + rootState.count;
}
}
}
const moduleB = {...}
const store = new Vuex.store({
modules{
a:moduleA,
b:moduleB
}
})
访问:
store.state.a
store.state.b
复制代码
命名空间:
namespaced:true
访问全局需加一些额外操做~vuex中的state用v-model
会报错!!应绑定事件回调mutation
~或用set
和get
双向绑定!