###VUE下
渲染函数和jsx
在vue中我们可以不用template来指定组件的模板,而是用render函数来创建虚拟dom结构,用这种方法优点就是性能高,缺点就是使用成本高,代码可读性较低,可以使用jsx来在render函数中创建,这样既提高了性能,又减少了成本
但是,我们在使用了vue-cli脚手架之后,因为脚手架中有对template标签转换虚拟dom的处理,所以,不需要使用jsx,我们也能高效的转换为createElement形式
Vue里组件的通信
通信:传参、控制(A操控B做一个事件)、数据共享
模式:父子组件间、非父子组件
-
父组件可以将一条数据传递给子组件,这条数据可以是动态的,父组件的数据更改的时候,子组件接收的也会变化
子组件被动的接收父组件的数据,子组件不要再更改这条数据了
-
父组件如果将一个引用类型的动态数据传递给子组价的时候,数据会变成双向控制的,子组件改数据的时候父组件也能接收到数据变化,因为子组件改的时候不是在改数据(地址),而是在改数据里的内容,也就是说引用类型数据的地址始终没有变化,不算改父组件数据
父子间数据共享(双向控制),基本不会使用,违背了单向数据流
-
父组件可以将一个方法传递给子组件,子组件调用这个方法的时候,就可以给父组件传递数据
父组件被动的接收子组件的数据
-
父组件可以将一个事件绑定在子组件的身上,这个事件的处理程序是父组件某一个方法,当子组件触发自己的这个被绑定的事件的时候,相当于触发了父组件的方法
父组件被动的接收子组件的数据
-
在组件间可以用过ref形成ref链,组件还拥有一个关系链(children,$root),通过这两种链;理论来说,任意的两个组件都可以互相访问,互相进行通信
任意组件通信,用的少...
-
event bus 事件总线 小天使 专注于非父子组件的通信,其实父子组件也可以使用,只是没有必要
在B组件的某个钩子函数为event_bus绑定一个事件,事件的处理程序是B想做的事情
在A组件的某一个操作里,触发event_bus绑定的事件
-
大量组件间数据共享的时候 vuex
组件的生命周期
每一个组件或者实例都会经历一个完整的生命周期,总共分为三个阶段:初始化、运行中、销毁
生命周期图示-
实例、组件通过new Vue() 创建出来之后会初始化事件和生命周期,然后就会执行beforeCreate钩子函数,这个时候,数据还没有挂载ね,只是一个空壳,无法访问到数据和真实的dom,一般不做操作
-
挂载数据,绑定事件等等,然后执行created函数,这个时候已经可以使用到数据,也可以更改数据,在这里更改数据不会触发updated函数,在这里可以在渲染前倒数第二次更改数据的机会,不会触发其他的钩子函数,一般可以在这里做初始数据的获取
-
接下来开始找实例或者组件对应的模板,编译模板为虚拟dom放入到render函数中准备渲染,然后执行beforeMount钩子函数,在这个函数中虚拟dom已经创建完成,马上就要渲染,在这里也可以更改数据,不会触发updated,在这里可以在渲染前最后一次更改数据的机会,不会触发其他的钩子函数,一般可以在这里做初始数据的获取
-
接下来开始render,渲染出真实dom,然后执行mounted钩子函数,此时,组件已经出现在页面中,数据、真实dom都已经处理好了,事件都已经挂载好了,可以在这里操作真实dom等事情...
-
当组件或实例的数据更改之后,会立即执行beforeUpdate,然后vue的虚拟dom机制会重新构建虚拟dom与上一次的虚拟dom树利用diff算法进行对比之后重新渲染,一般不做什么事儿
-
当更新完成后,执行updated,数据已经更改完成,dom也重新render完成,可以操作更新后的虚拟dom
-
当经过某种途径调用$destroy方法后,立即执行beforeDestroy,一般在这里做一些善后工作,例如清除计时器、清除非指令绑定的事件等等
-
组件的数据绑定、监听...去掉后只剩下dom空壳,这个时候,执行destroyed,在这里做善后工作也可以
vue-cli脚手架
现在使用前端工程化开发项目是主流的趋势,也就是说,我们需要使用一些工具来搭建vue的开发环境,一般情况下我们使用webpack来搭建,在这里我们直接使用vue官方提供的,基于webpack的脚手架工具:vue-cli
安装方法:
# 全局安装 vue-cli
npm install --global vue-cli
# 创建一个基于 webpack 模板的新项目
vue init webpack my-project
//init之后可以定义模板的类型
# 安装依赖,走你
cd my-project
npm install
npm run dev
模板类型:
simple 对应的是一个超级简单的html文件
webpack 在配置的时候可以选择是否需要vue-router
注意的是,模板创建的时候会询问使用需要使用ESLINT来标准化我们的代码
在脚手架中,开发目录是src文件夹,build负责打包的,config是负责配置(内置服务器的端口、proxy代理),static是静态目录,test是测试
src中main.js是入口文件,在里面创建了一个根实例,根实例的模板就是根组件App的模板,其他的组件都在根组件里面进行嵌套实现。
每一个组件都是一个单文件组件,这种文件会被webpack利用vue-loader的工具进行编译
template部分负责写组件的模板内容,script中创建组件。style里写组件的样式
assets目录也是静态目录,在这个目标中的文件我们使用相对路径引入,而static目录中的文件使用绝对地址来引入
在style上添加scoped能使这个style里的样式只作用于当前的组件,不加scoped就是全局样式
习惯于在App.vue根组件的style里写全局样式,而每个组件的style最好都是局部的
配置sass编译环境
vue-cli没有内置sass编译,我们需要自己修改配置
-
下载对应工具:node-sass(4.0.0) sass-loader
-
在build目录下的webpack.base.conf.js中的module.rule里添加如下配置
{
test: /\.scss$/,
loader:'style-loader!css-loader!sass-loader'
}
- 在需要使用scss代码的组件的style标签中添加 lang='scss'
vue-router
现在的应用都流行SPA应用(single page application)
传统的项目大多使用多页面结构,需要切换内容的时候我们往往会进行单个html文件的跳转,这个时候受网络、性能影响,浏览器会出现不定时间的空白界面,用户体验不好
单页面应用就是用户通过某些操作更改地址栏url之后,动态的进行不同模板内容的无刷新切换,用户体验好。
Vue中会使用官方提供的vue-router插件来使用单页面,原理就是通过检测地址栏变化后将对应的路由组件进行切换(卸载和安装)
简单路由实现:
- 引入vue-router,如果是在脚手架中,引入VueRouter之后,需要通过Vue.use来注册插件
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
- 创建router路由器
new Router(options)
- 创建路由表并配置在路由器中
var routes = [
{path,component}//path为路径,component为路径对应的路由组件
]
new Router({
routes
})
- 在根实例里注入router,目的是为了让所有的组件里都能通过this.route来使用路由的相关功能api
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
})
-
利用router-view来指定路由切换的位置
-
使用router-link来创建切换的工具,会渲染成a标签,添加to属性来设置要更改的path信息,且会根据当前路由的变化为a标签添加对应的router-link-active/router-link-exact-active(完全匹配成功)类名
<router-link to="main">main</router-link>
<router-link to="news">news</router-link>
.router-link-active{
color:red;
}
多级路由:
在创建路由表的时候,可以为每一个路由对象创建children属性,值为数组,在这个里面又可以配置一些路由对象来使用多级路由,注意:一级路由path前加'/'
const routes = [
{path:'/main',component:AppMain},
{path:'/news',component:AppNews,children:[
{path:'inside',component:AppNewsInside},
{path:'outside',component:AppNewsOutside}
]},
]
二级路由组件的切换位置依然由router-view来指定(指定在父级路由组件的模板中)
<router-link to='inside'>inside</router-link>
<router-link to='outside'>outside</router-link>
<router-view></router-view>
默认路由和重定向:
当我们进入应用,默认像显示某一个路由组件,或者当我们进入某一级路由组件的时候想默认显示其某一个子路由组件,我们可以配置默认路由:
{path:'',component:Main}
当我们需要进入之后进行重定向到其他路由的时候,或者当url与路由表不匹配的时候:
{path:'',redirect:'/main'}
///...放在最下面
{path:'**',redirect:'/main'},
命名路由
我们可以给路由对象配置name属性,这样的话,我们在跳转的时候直接写name:main就会快速的找到此name属性对应的路由,不需要写大量的urlpath路径了
动态路由匹配
有的时候我们需要在路由跳转的时候跟上参数,路由传参的参数主要有两种:路径参数、queryString参数
路由参数需要在路由表里设置
{path:'/user/:id',component:User}
上面的代码就是给User路由配置接收id的参数,多个参数继续在后面设置
在组件中可以通过this.$route.params来使用
queryString参数不需要在路由表设置接收,直接设置?后面的内容,在路由组件中通过this.$route.query接收
router-link
<router-link> 组件支持用户在具有路由功能的应用中(点击)导航。 通过 to 属性指定目标地址,默认渲染成带有正确链接的 <a> 标签,可以通过配置 tag 属性生成别的标签.。另外,当目标路由成功激活时,链接元素自动设置一个表示激活的 CSS 类名。
router-link的to属性,默认写的是path(路由的路径),可以通过设置一个对象,来匹配更多
:to='{name:"detail",params:{id:_new.id},query:{content:_new.content}}'
name是要跳转的路由的名字,也可以写path来指定路径,但是用path的时候就不能使用params传参,params是传路径参数,query传queryString参数
replace属性可以控制router-link的跳转不被记录\
active-class属性可以控制路径切换的时候对应的router-link渲染的dom添加的类名
编程式导航
有的时候需要在跳转前进行一些动作,router-link直接跳转,需要在方法里使用$router的方法
router.push = router-link:to
router.replace = router-link:to.replace
router.go() = window.history.go
路由模式
路由有两种模式:hash、history,默认会使用hash模式,但是如果url里不想出现丑陋hash值,在new VueRouter的时候配置mode值为history来改变路由模式,本质使用H5的histroy.pushState方法来更改url,不会引起刷新,但是需要后端进行路由的配置
路由钩子
在某些情况下,当路由跳转前或跳转后、进入、离开某一个路由前、后,需要做某些操作,就可以使用路由钩子来监听路由的变化
全局路由钩子:
router.beforeEach((to, from, next) => {
//会在任意路由跳转前执行,next一定要记着执行,不然路由不能跳转了
console.log('beforeEach')
console.log(to,from)
//
next()
})
//
router.afterEach((to, from) => {
//会在任意路由跳转后执行
console.log('afterEach')
})
单个路由钩子:
只有beforeEnter,在进入前执行,to参数就是当前路由
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
路由组件钩子:
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
命名视图
有时候想同时(同级)展示多个视图,而不是嵌套展示,例如创建一个布局,有 sidebar(侧导航) 和 main(主内容) 两个视图,这个时候命名视图就派上用场了。你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view 没有设置名字,那么默认为 default。
<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>
一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。确保正确使用 components 配置(带上 s):
const router = new VueRouter({
routes: [
{
path: '/',
components: {
default: Foo,//默认的,没有name的router-view
a: Bar,
b: Baz
}
}
]
})
prop将路由与组件解耦
在组件中接收路由参数需要this.$route.params.id,代码冗余,现在可以在路由表里配置props:true
{path:'detail/:id',component:AppNewsDetail,name:'detail',props:true}
在路由自己中可以通过props接收id参数去使用了
props:['id']
Axios 数据交互工具
vue官方宣布在2.0版本中不再对Vue-resource进行维护了,推荐使用axios工具
注意,axios默认配置不会设置session-cookie,需要进行配置
axios.defaults.withCredentials = true
详细请看文档
响应式原理
因为vue是mvvm的框架,所以当数据变化的时候,视图会立即更新,视图层产生操作后会自动通知vm来更改model,所以我们可以实现双向数据绑定,而其中的原理就是实例会将设置的data逐个遍历利用Object.defineProperty给数据生成getter和setter,当数据变化地方时候setter会监听到并且通知对应的watcher工具进行逻辑运算会更新视图
vuex借鉴了flux和redux的思想,但是flux和redux是独立且完整的架构,vuex是耦合与vue框架的,所以使用成本要比flux、redux低
声明式渲染
在vue中,我们可以先在vue实例中声明数据,然后通过{{}}等方式渲染在dom中
Vuex
Vuex是vue官方的一款状态管理工具,什么是状态呢?我们在前端开发中有一个概念:数据驱动,页面中任意的显示不同,都应该有一条数据来控制,而这条数据又叫做state,状态。
在vue中。组件间进行数据传递、通信很频繁,而父子组件和非父子组件的通信功能也比较完善,但是,唯一困难的就是多组件间的数据共享,这个问题由vuex来处理
Vuex的使用:
- 创建store:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
//可以设置store管理的state/getter,mutations,actions
const store = new Vuex.Store({
})
- 设置state
state就是一个纯对象,上面有一些状态挂载,而且一个应用应该只有一个数据源:单一状态树、唯一数据源
import state from './modules/state'
//可以设置store管理的state/getter,mutations,actions
const store = new Vuex.Store({
state
})
- 在根实例里配置store
这样,我们就可以在任意的组件中通过this.$store来使用关于store的api
import store from './store'
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App }
})
- 在组件中使用state
因为在组件中可以通过this.store.state来使用state中管理的数据
data(){
return {
num:this.$store.state.num
}
},
但是我们发现,这样使用的话,当state的数据更改的时候,vue组件并不会重新渲染,不会触发组件的相关生命周期函数
也就是说,如果想要在组件中响应式的使用的时候,我们需要通过计算属性(computed)来使用
computed:{
num(){
return this.$store.state.num
}
}
这样的写法很无趣,而且如果使用的状态较多会产生冗余的感觉,所以vuex提供了mapState辅助函数,帮助我们在组件中获取并使用vuex的store中保存的状态
所以我们可以这样写:
computed:mapState(['num']),
但是如果组件中已经有了num这个数据了,而state中的数据名字也叫num就会照成冲突,这个时候我们可以在组件使用state的时候,给状态起个别名:
computed:mapState({
// _num:'num',//键名为别名,值字符串代表的是真正的状态
_num(state){//方法名为别名,函数体里还可以对真正的状态做出一些处理
return state.num
}
}),
但是,有的时候我们在组件中还有自己的业务逻辑需要用到计算属性:
computed:{
a(){
return num+1
},
...mapState({
// _num:'num',//键名为别名,值字符串代表的是真正的状态
_num(state){//方法名为别名,函数体里还可以对真正的状态做出一些处理
return state.num
}
}),
},
- getters
有的时候,我们需要根据state中的某一个状态派生出一个新的状态,例如,我们state中有一个num,在某些组件中需要用到是num的二倍的一个状态,我们就可以通过getters来创建
const getters = {
doublenum(state){
return state.num*2
}
}
创建了之后,在组件中通过this.$store.getters来获取里面的数据
当然vuex也提供了mapGetters辅助函数来帮助我们在组件中使用getters里的状态,且,使用的方法和mapState一模一样
- 使用mutations更改state
我们不能直接在组件中更改state:this.$store.state.num=2,而是需要使用mutations来更改,mutations也是一个纯对象,里面包含很多更改state 的方法,这些方法的形参接收到state,在函数体里更改,这时,组件用到的数据也会更改,实现响应式。
但是我们也不能直接调用mutations 的方法,需要使用this.$store.commit来调用,第一个参数为调用的方法名,第二げ参数为传递参数
const mutations = {
increment(state){
state.num++
}
}
vuex提供了mapMutations方法来帮助我们在组件中调用mutations 的方法,使用方法和mapState、mapGetters一样
- 使用actions来处理异步操作
Action 类似于 mutation,不同在于:
Action 提交的是 mutation,而不是直接变更状态。
Action 可以包含任意异步操作。
也就是说,如果有这样的需求:在一个异步处理之后,更改状态,我们在组件中应该先调用actions,来进行异步动作,然后由actions调用mutation来更改数据
const actions = {
[CHANGE_NUM]({commit}){
alert(1)
setTimeout(() => {
let num = Math.floor(Math.random()*10)
//调用mitations的方法
commit(CHANGE_NUM,num)
}, 1000);
}
}
``
如上,actions的方法中可以进行异步的动作,且形参会接收store,从中取出commit方法用以调用mutations的方法
在组件中通过this.$store.dispatch方法调用actions的方法
当然也可以使用mapMutations来辅助使用
组件使用数据且通过异步动作更改数据的一系列事情:
1.生成store,设置state
2.在根实例中注入store
3.组件通过计算属性或者mapState来使用状态
4.用户产生操作,调用actions的方法,然后进行异步动作
5.异步动作之后,通过commit调用mutations的方法
6.mutations方法被调用后,更改state
7.state中的数据更新之后,计算属性重新执行来更改在页面中使用的状态
8.组件状态被更改...创建新的虚拟dom......
9.组件的模板更新之后重新渲染在dom中
vuex的使用:
目前市场上有两种使用vuex的情况,
第一种:将需要共享、需要管理的状态放入vuex中管理,也就是说在必要时使用
第二种:将所有的数据都交由vuex管理,由vuex来承担更多的责任,组件变得更轻量级,视图层更轻
---
##### 自定义指令
在实现回到顶部功能的时候,我们写了一个backTop组件,接下来需要通过监听window.scroll事件来控制这个组件显示隐藏
因为可能会有其他的组件会用到这样的逻辑,所以将此功能做成一个自定义指令:
根据滚动的距离控制一个数据为true还是为false(v-scroll-show)
问题:
唯一需要注意的是,在指令的钩子函数中我们可以访问到el,也就是使用指令的标签,但是我们不能直接更改value(指令的值所代表的数据)
所以我们使用引用类型来进行地址的传递来解决这个问题
接下来有写了一个v-back-top指令,就是将回到顶部功能做成一个指令,哪个组件或者dom需要使用到回到顶部,就加上这个指令就可以,设置不同的参数来控制在不同的情况下触发
##### Vue的组件库
组件库就是通用组件的集合
pc:element-ui iview
mobile: mint-ui
##### nextTick
当我们在使用一些插件的时候,经常需要在dom更新完成后进行必要操作,但是在vue中提供的api只有updated钩子函数,而在这个函数里,任意数据的变化导致的dom更新完成都会触发,所以很可能会造成无关数据的影响,而使用监听的话只能监听到数据的变化,此时dom还没有更新,我们只能强行使用setTimeout来处理
这里推荐大家使用nextTick全局方法:
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
eq:
getBillBoards(){
axios.get(this.$root.config.host+'mz/v4/api/billboard/home',{
params:{__t:Date.now()}
}).then(res => {
console.log(res.data.data.billboards)
this.billboards = res.data.data.billboards
//当数据更新,dom循环完成后,执行回调
Vue.nextTick(function () {
new Swiper('.app-home-banner',{
loop:true
})
})
})
}
##### keep-alive
在component组件、router-view外面包裹上keep-alive的话,就会对组件进行缓存,当切换回来的时候,组件会立即渲染,理论来说,切换组件的时候其实会把上一个组件销毁,使用了keep-alive则不会
设置include、exclude属性控制有选择的缓存
include匹配到的组件会被缓存,exclude匹配到的不会被缓存
值可以为逗号隔开的字符串include = 'a,b';正则:include = '/a|b/';数组:include=['a','b']
使用keep-alive缓存的组件连带它的子组件们都会拥有activated、deactivated钩子函数,会在切换回来和要切换出去的时候触发
比如,main做了缓存,但是main的banner我们希望每次都去重新获取数据,所以就在banner的activated里获取数据
### Vue试题分析
1. v-for可以实现数据遍历显示,不仅可以遍历数组,也可以遍历对象,还可以从数值中取值:
v-for='n in 10' n会打印1-10
2. vue的生命周期钩子:
通用:beforeCreate/created/beforeMount/mounted/beforeUpdate/updated/beforeDestroy/destroyed
路由守卫:beforeRouteEnter/beforeRouteUpdate (2.2 新增)/beforeRouteLeave
keep-alive:activated/deactivated
3. v-if v-show
v-if是真正的条件渲染,会确保在切换中条件块内的事件监听、子组件都会适当的被销毁和重建
v-show总是将节点渲染在dom中,只是基于css:display来控制节点的显示和隐藏
v-if有更高的切换开始,v-show有更高的初始渲染开销
v-if是惰性的,初始条件为假,就不会渲染
4. axios相关
axios请求的时候不会带上cookie,不会影响带宽,可以通过withCredentials:true来设置
对axios 的请求头进行设置:
axios.defaults.headers = {'Content-Type':'...'}
vue2.0不在更新维护vue-resource,官方推荐使用axios
axios拦截器可以拦截请求和响应,在then、catch之前拦截
6. 组件实例的作用域是孤立的,意味着不能(不应该)在子组件模板里直接引用父组件的数据,要让子组件使用父组件数据的话,需要通过props来将父组件的数据传递给子组件,子组件不能也不应该修改父组件传入的数据,但是可以通过传入引用类型的数据来实现数据共享
7.为了让组件可以组合,我们需要一种方式来混合父组件的内容与子组件自己的模板。这个过程被称为内容分发 (即 Angular 用户熟知的“transclusion”)。Vue.js 实现了一个内容分发 API,参照了当前 Web Components 规范草案,使用特殊的 <slot> 元素作为原始内容的插槽。
a-template:
<p>hello world</p>
<b>
<h1>hello world</h1>
</b>
b-template:
<slot></slot>
....
8. 如果把切换出去的组件保存在内存中,保留状态避免重新渲染,可以使用keep-alive
include exclude
9. 注册方式:
全局:Vue.component(name,Vue.extend({}))
局部:{ components:{name:Vue.extend({})} }
10. 事件总线实现非父子组件通信
//创建bus
let bus = new Vue()
//a
new Vue({
template:'...',
mounted(){
bus.$on('emit-a',function(){
alert(1)
})
}
})
//b
new Vue({
template:'...',
methods:{
emitA(){
bus.$emit('emit-a')
}
}
})
//当b组件的emitA方法被调用的时候,A组件就会执行alert(1)
11. methods和计算属性的区别
假设我们有一个数据为num,还希望拥有一个数据为doublenum,而且希望doublenum的值永远都是num的二倍
方法:
* 因为是直接显示在模板中,也就是说,我们可以来一个doublenum的方法,这个方法返回num的二倍,将这个方法放到模板中的某个地方执行 {{doublenum()}}
但是,当无关的例如一个str的数据更改的时候,组件会重新创建虚拟dom树,与上一次的虚拟dom树对比之后重新渲染,这个时候在重新渲染模板的时候doublenum函数会被再次的调用,造成不必要的性能浪费
* 创建一个doublenum数据,使其初始值为num的二倍,然后利用watch来监听这两个数据,在改变的时候更改对应的数据,但是需要初始的为doublenum赋值为num的二倍,如果num是动态获取到的,doublenun赋值会更繁琐
* computed计算数据,我们可以利用computed来创建一条新的doublenum数据。并且设置它的getter和setter,并与num建立关系,且computed会缓存,在重新渲染的时候,不会重新执行getter和setter
computed:{
doublenum:{
get(){
return this.num*2
},
set(val){
this.num = val/2
}
}
}
12. 绑定class的对象语法和数组语法
<a :class="{a:true,b:false,c:1}"> => </a> => <a class='a c'></a>
data(){
return {
c:'c'
}
}
<a :class = '["a","b",c]'></a> => </a> => <a class='a b c'></a>
13.
new Vue({
el:"#example-3",
methods:{
say(str){
alert(str)
}
}
})
14. 单向数据流
prop是单向绑定的,父组件属性变化,传递给子组件,但是,子组件数据变化,不能直接传递给父组件,也就是数据的流行是从父组件流向子组件的,为了防止子组件修改父组件的数据(会让应用的数据流变的更难开发、更新、维护)
使用了vuex工具的时候,store中数据在组件中使用的过程也是单向数据流,state->vue component->actions->mutations->state->vue component
15. this.$router.push/replace({name:'user',params:{userId:1})
this.$router.push/replace({path:'/register',query:{plan:private})
##### key相关
当数据改变之后,vue会创建新的虚拟dom来和原来的虚拟dom做对比,在创建新的虚拟的dom的时候,会根据key来查找在原来的虚拟dom中有没有某个部分,如果原来的有,这次的也需要,就会实现复用,而且在做diff对比的时候,如果有key会加快对比的查找速度,提高性能
尽量循环的时候不要将key设置为数组的索引,因为当删除某一个元素的时候,就会导致删除位置下面的所有元素的key值都与上一次虚拟dom的key值不同,导致复用失败,这个时候我们最好使用关键的唯一的,例如id这样的数据作为key
如果数据变化只是值的变化而不是条数和位置的变化,可以使用索引作为key
##### Vue.use()
Vue.use会查找插件对象里的install方法去执行,并且给install方法里传入Vue对象
var a = {
install(Vue){
Vue.component("my-a",{...})
}
}
Vue.use(a)
##### 进入域后根据不同的情况显示不同的页面(PC/MOBILE)
很多情况下,一个应用会有PC和移动端两个版本,而这两个版本因为差别大,内容多,所以不能用响应式开发但是单独开发,而域名只有一个,用户进入域后直接返回对应设备的应用,做法主要有两种:
1. 前端判断并跳转
进入一个应用或者一个空白页面后,通过navigator.userAgent来判断用户访问的设备类型,进行跳转
2. 后端判断并响应对应的应用
用户地址栏进入域的时候,服务器能接收到请求头上包含的userAgent信息,判断之后返回对应应用
---
function foo(){// 第16行
getName = function(){console.log(1)}
return this
}
foo.getName = function(){console.log(2)}
foo.prototype.getName = function(){console.log(3)}
var getName = function(){console.log(4)}
function getName(){console.log(5)}
foo.getName()//2
//foo是一个函数,也可以说是一个对象,所以它也可以挂载一些属性和方法,18行在其上挂载了一个getName方法
//执行的结果是
getName()//4
//21行有一个全局函数,全局函数声明提前后被20行的getName覆盖,所以输出4
foo().getName()//1
//foo()执行完成后,将全局的getName也就是window.getName给更改后返回this,而在这里this执行的就是window,所以最后执行的就是window.getName,所以输出1
getName()//1
//在上面已经更改全局的getName,所以依然是1
new foo.getName()//2
//new 操作符在实例化构造器的时候,会执行构造器函数,也就是说,foo.getName会执行,输出2
new foo().getName()//3
//new操作符的优先级较高,所以会先new foo()得到一个实例,然后再执行实例的getName方法,这个时候,实例的构造器里没有getName方法,就会执行构造器原型上的getName方法
new new foo().getName()//3
//先执行new foo()得到一个实例,然后在new 这个实例的getName方法,这个时候会执行这个方法,所以输出3
//除了本地对象的方法,其他的函数都能new
---