Vue2.x 总结

Vue2.x 总结

Vue 是一套用于构建用户界面的渐进式框架

也意味着,既能够把VUE做为该应用的一部分嵌入到一个现成的服务端应用,或者在先后端分离的应用中,利用Vue 的核心库及其生态系统,把更多的逻辑放在前端来实现。html

A Progressive Framework

渐进式框架前端

与Vue相比,React学习曲线陡峭,在学习React以前,须要了解JSX和ES2015,固然入门后,发现还要学习React全家桶。而Vue就能够在简单阅读了文档后,开始构建应用程序。vue

这就要得益于Vue主张的 渐进式
能够简单看下官方给出这张图:react

a progressive framework

能够看出来,主要是介绍了Vue设计思想,就是框架作分层设计,每层均可选,能够单独引入,为不一样的业务需求制定灵活的方案。主张最少,不会多作职责之外的事。webpack

Vue做者尤雨溪的观点,Vue设计上包括的解决方案不少,可是使用者彻底不须要一上手,就把全部东西全都用上,由于彻底没有必要,通常都是根据项目的复杂度,在核心的基础上任意选用其余的部件,不必定要所有整合在一块儿。git

这样渐进式的解决方案,使得学习成本大大减小了。github

声明式渲染

也就是说,DOM状态只是数据状态的一个映射,基本全部的框架都已经认同了这个见解,Vue也是主张 数据驱动状态web

说到这里,基本都会提到如今主流的MVVM的模式。
mvvmajax

采用了双向数据绑定的思想,基本能够分为三层:vue-router

  • M(Model,模型层),负责业务数据相关。
  • V(View,视图层),视图相关,展现给用户的交互界面,同时捕获用户的操做
  • VM(ViewModel, V与M链接的桥梁,也能够看作控制器)。

基于这个思想,Vue从一开始就利用ViewModel与view,model进行交互
Vue mvvm
ViewModel是Vue.js的核心,它是一个Vue实例,做用在某个HTML元素上,通常都是指定 id= app的元素,图中 的DOM listenersData Bindings能够看作两个工具,它们是实现双向数据绑定的关键。

从用户(View)角度看,DOM Liisteners利用在相应的元素上添加事件绑定,捕获用户的点击,滑动等手势动做,在事件流中改变对应的Model。好比 经常使用的 v-model 指令,就是捕获表单元素的inputchange等事件,改变相应的绑定值。

从Model方向看,Data Bindings则将操做的数据变化,反应到view上。好比经过ajax 从后台获取的数据,能够刷新数据列表,反应到用户界面。这也是实现双向数据绑定的关键。

Vue2中是经过Object.definedProperty方法中定义的getters和 setters构造器来实现数据响应的。能够简化下源码中的实现:

Object.defineProperty(obj, key, {
         enumerable: true,
         configurable: true,
         get: function reactiveGetter () {
            return value
         },
         set: function reactiveSetter (newVal) {
             var value = getter ? getter.call(obj) : val;
             /* eslint-disable no-self-compare */
             if (newVal === value || (newVal !== newVal && value !== value)) {
               return
             }
             /* eslint-enable no-self-compare */
             if ("development" !== 'production' && customSetter) {
               customSetter();
             }
             if (setter) {
               setter.call(obj, newVal);
             } else {
               val = newVal;
             }
             childOb = !shallow && observe(newVal);
             dep.notify();
           }
       });
   }

经过这种方法定义对象obj上的某个属性,每次获取属性值的时候就,会主动触发get对应的回调函数,而后给该属性赋值时,就会触发里面的set对应的回调函数,在set回调函数里面,加入了dep.notify()方法,而后能够看下这个方法

notify () {
 // stabilize the subscriber list first
 const subs = this.subs.slice()
 for (let i = 0, l = subs.length; i < l; i++) {
   subs[i].update()
 }
}

里面的定义的常量subs每次深拷贝this.subs数组,数组里面保存的就是全部的subscriber订阅者,对应的发布者就是obj里面对应的属性,或者说是Vue中的data值。通知全部的订阅者,数据更新了。原生js实现发布订阅模式(publish/Subscribe),能够参考这里

经常使用基础语法

hello world

<!DOCTYPE html>
  <html>
    <head>
      <meta charset="utf-8">
      <title>hello world</title>
      <script src="https://gw.alipayobjects.com/as/g/h5-lib/vue/2.4.4/vue.min.js"></script>
    </head>
    <body>
      <div id="app">
        {{message}}
      </div>
      <script>
        var app = new Vue({
          el:"#app",
          data:{
            message:'hello vue'
          }
        })
      </script>
    </body>
  </html>

这样就简单建立了一个Vue 应用,数据message 和DOM页面产生了关联,相似html模板引擎,把相应的数据渲染到页面中。

指令

指令 (Directives) 是带有 v- 前缀的特殊属性,这些特殊属性能够响应式的做用域DOM,

  • v-if 接受Boolean 类型,好比: <p v-if = "seen">如今你看到我了</p> ,经过seen的真假来插入/移除< p>元素。 这里判断的时候使用 === 全等,seen = “false” 的时候,也会插入
  • v-bind,响应式的更新HTM属性。完整形式<a v-bind:href="url">...</a> 。 缩写形式<a :href="url">...</a>。 经常使用于改变dom的style, class ,href ,src 等属性。 动态绑定的属性能够写成 :属性名="属性值"
  • v-on,绑定点击事件,好比 完整形式<a v-on:click="doSomething">...</a>,简写形式 <a @click="doSomething">...</a>, doSomething对应的指向methods里面定义的函数。 注意,除非在须要传递参数的时候,写成 @click = "doSomething($event,args1,args2)",$event表明事件对象,args表明自定义参数

style or class

v-bind用于classstyle时,Vue.js作了专门的加强,表达式结果的类型除了字符串以外,还能够是对象或数组。

绑定HTML Class

  • 直接赋值。

    < div :class="className"> </div>
    data:{
       className:"div-class"
    }

    结果:

    <div class="div-class"></div>
  • 对象语法。

    < div class="static" :class="{active:isActive,'text-danger':hasError}" /></div>
    
       data: {
         isActive:true,
         hasError:false,
       }

    结果:

    <div class="static active" ></div>
  • 数组语法,

    <div :class="['one',bTwo?'two':'three']" </div>
    
      data:{
         bTwo:true
      }
    
      <style>
         .one{}
         .two{}

    结果:

    <div class='one two'></div>

绑定内联样式

  • 对象语法

    <div :style = "{color:activeColor,fontSize:fontSize+'px'}"></div>
         data: {
           activeColor: 'red',
           fontSize: 30
         }

    结果:

    <div style="color:red:font-size:30px;"></div>
  • 数组语法

    <div v-bind:style="[baseStyles, overridingStyles]"></div>
    
      data:{
        baseStyles:{
          color:'red'
        },
        overridingStyles:{
          fontSize:'30px'
        }
    
      }

    结果:

    <div style="color:red:font-size:30px;"></div>

条件渲染

  • v-if

    一样也是一个指令,添加到一个元素上,对应利用 === 全等判断绑定的值truefalse来决定是否渲染里面的节点。
    能够与它一块儿使用的指令有 v-else ,v-else-if,v-else 元素必须紧跟在都有v-if或者v-else-if的元素后面

    <div v-if= "num===0">
       0
       </div>
       <div v-else-if ="num ===1">
       1
       </div>
       <div v-else>
          not 0/1
       </div>
    
       data:{
         num:3
       }

    结果:

    <div>
        not 0/1
    </div>
  • v-show

    根据条件展现元素的选项,简单的切换元素的内联样式display

    适用场景:

    • 页面复用的(modal)弹出窗,利用v-show,控制显示或者隐藏。参考这里
    • tab页面切换的时候,不一样页面不相互影响,利用v-show不会销毁元素,参考这里

列表渲染

  • v-for 把一个数组对应为一组元素,推荐给每一个列表,添加惟一标识的key

    <div v-for="(item,idx) in items" :key="idx">
         {{idx}} --- {{item.product}}
      </div>
    
      data:{
        items:[
         {product:"foo"},
         {product:"bar"}
        ]
      }

    结果:

    <div>
          0 --- foo
    </div>
    <div>
        1 --- bar
    </div>
  • 数组更新检测

    包含一组观察数组变异方法,用来触发试图更新:
    push(),pop(),shift(),unshift(),splice(),sort(),reverse()

    利用索引给数组赋值或者手动修改数组的长度,都不会被检测到更新
    替代方案:

    Vue.set(example1.items,indexOfItem,newValue)
     // 或者
     example1.items.splice(indexOfItem,1,newValue)
    
     // 改变数组的长度:
     example1.items.splice(newLength)

    对于对象中的属性添加或删除,也可使用Vue.set方法,或者在定义的Vue实例内部,使用this.$set,this.$set只是全局Vue.set的别名

    Vue.set(object,key,value)

v-model 表单绑定

使用v-model在表单input<textarea>元素上建立双向数据绑定。

<input v-model = "message" placeholer= "edit me">
 <p>Message is {{message}}</p>

这样input输入框中的值就与P标签中的内容绑定了,一样也适用textareacheckbox,
radio,select等表单。

实质上,v-model只是语法糖。

<input v-model = "something"

对应的完整形式:

<input
       v-bind:value="something"
       v-on:input="something = $event.target.value">

表单数组校验。
利用修饰符 .number进行数字校验,是最实用的方法,在v-model上添加number修饰符。

<input v-model.number="age" type= "number" >

组件通讯

组件能够用来扩展HTML,封装可重用的代码,全部的组件都是Vue的实例。

命名: 建议遵循W3C规则(小写,而且包含一个短杆)

组件组合:使用中最多见的是造成父子组件的关系,组件A在它的模板中使用了组件B,那么他们之间就须要通讯。组件间通讯的关系能够用下面的图示代表:
props-events

归纳为: prop 向下传递,事件向上传递。

  • 利用Prop 传递数据,同时借助.sync,进行双向数据通讯

    父组件的数据要经过Prop才能下发到子组件中。
    prop 属性命名:在使用camelCase(驼峰式命名)的prop须要装换成对应的kebab-case(短横线分隔式命名)。同时,也能够绑定动态的Prop传递给子组件。

    而后,子组件中prop值改变,是没法反应到父组件中的。在Vue1.x中使用.sync修饰符能够提供双向绑定,可是违背了单向数据流的思想,在2.0中就移除了,但在2.3.0中做为一种语法糖的形式引入了

    Vue.component('child',{
        props:['myMessage'],
        template:'<span @click="handleClick">{{myMessage}}</span>',
       methods:{
          handleClick:function(){
            this.$emit("update:myMessage","message from child")
          }
       }
      })
    
      <!--  在HTML 中使用时   .sync的语法糖  -->
      <child :my-message.sync="parentMsg"></child>

    点击子组件中的span,就能够改变父组件中prop绑定的parentMsg值。.sync语法也会被扩展成为

    <child :my-message="parentMsg" @update:myMessage = "val => parentMsg = val"

    里面 @update:myMessage 就是绑定了自定义事件,回过来看下上面父子通信的规则 prop向下传递,事件向上传递,也很是符合。

  • 非父子组件通讯

    官方推荐使用空的Vue实例做事件总线

    var bus = new Vue();
    
        // 在A 组件中触发了事件
         bus.$emit("change",1);
    
        // 在B 组件中监听事件
        bus.$on('change',function(id){})

状态管理

组件通讯变得复杂时,就要考虑使用全局状态管理,Vue也提供了vuex状态管理库。

Vuex 是一个专门为Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的全部组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

固然,使用Vuex并非首选,只有在构建中大型单页面应用时,考虑到全局的状态管理,天然就会想到Vuex。
下面这张图,表示状态管理“单向数据流”的理念
vuex

核心概念包括(简单计数器为例):

  • state: 做为单一状态树,惟一的数据源,而且每一个应用仅仅包含一个store实例,通常经过计算属性获取某个状态。

    state:{
      count:0
    },
  • getter: 至关于store的计算属性,数据源发生变化时,返回通过处理后的值,

    getters:{
       getState:state=>{
         return state.count
       }
    },
  • mutation: 相似于事件,对应的回调函数到状态进行处理,必须经过store.commit的方式手动触发

    mutations:{
      increment:state => state.count++,
      decrement:state => state.count--
    },
  • actions: 利用commit提交mutation,能够执行异步操做,经过 store.dispatch方式触发

    // 模拟异步请求
    var delay = (timeout,cb) => new Promise(resolve => setTimeout(()=>{cb(); resolve("test incrementAsync")},timeout));
    
    actions:{
      incrementAsync({commit}) {
          return delay(600,function(){commit("increment")})
    
      },
    }
  • module: 当store对象比较庞大的时候,能够考虑将store分隔成模板。每一个模块拥有本身的state、mutation、action、getter。不多状况下使用

把全部部分组合起来,就构成一个简单的计数器:

var delay = (timeout,cb) => new Promise(resolve => setTimeout(()=>{cb(); resolve("test incrementAsync")},timeout));
var store = new Vuex.Store({
  state:{
    count:0
  },
  getters:{
     getState:state=>{
       return state.count
     }
  },
  mutations:{
    increment:state => state.count++,
    decrement:state => state.count--
  },
  actions:{
    incrementAsync({commit}) {
        return delay(600,function(){commit("increment")})
    },
    decrementAsync({commit}) {
        return delay(600,function(){commit('decrement')});
    }
  }
})

而后组件中触发actions,就能够

store.dispatch('decrementAsync').then(() => {
// ...
})

在浏览器中,使用vue-devtool,试下时间旅行功能。
vue-devtool

页面路由

使用Vue.js建立单页面应用,就可使用vue-router,目前版本是3.0.1,把组件映射到对应的路由,经过改变url来渲染不一样的页面。官方中文文档

vue-router 默认hash模式,每次url只会改变#后面对应的值,页面就不会从新加载,而且也不须要服务器端做任何配置。

若是使用路由的history模式,url就会正常http://yoursite.com/user/id,只须要添加配置mode:'history',同时须要后端配置,否则页面从新刷新,会匹配不到任何资源。

不一样模式下的服务器配置及生产环境部署,能够参考vue、react等单页面项目应该这样部署到服务器.

基础概念:

  • < router-link>

    <router-link to="/foo">Go to Foo </router-link>
      <router-link to= "/bar">Go to Bar</router-link>

    使用router-link 组件导航,经过传入to属性指定连接,至关于原生的a 标签。

  • < router-view>
    路由出口,路由匹配到的组件将渲染在这里

    <router-view></router-view>

    能够在上面添加一些过渡效果

    <transiton name="slide">
         <router-view></router-view>
      </transiton>
  • 初始化路由配置

    若是使用vue-cli脚手架构造项目,在init的时候,会出现选项提示用户安装路由

    vue-router

    确认后,自动生成src/router.js文件,相关路由配置文件就能够写在里面。

    // 首先引入不一样的vue组件(默认安装了[vue-loader](https://vue-loader.vuejs.org/zh-cn/start/spec.html),每一个.vue文件当作一个完整的组件)
      import components1 from "./page/xx.vue"
      import components2 from "./page/xx2.vue"
    
      // router 数组用来定义路由配置,非嵌套路由
      const routers = [
         {path:'/foo',component:components1},
         {path:'/bar',component:components2}
      ]
    
     // 最后抛出这个配置数组
      export default routes;

    注入到router配置参数里面

    const app = new Vue({
            router
       }).$mount("#app")

    存在嵌套路由的时候,须要使用 children配置,好比上面的components1组件内部包含本身的嵌套<router-view>,就可使用嵌套路由。

    const routers = [
         { path:'/foo',
           component:components1,
           children:[
             // 当  /foo/detail 匹配成功后,component3 会被渲染在components1中 <router-view> 的位置
              {  path:'detail',
                  component:component3
              },
            // 若是 /foo 匹配成功,没有匹配子路由,默认就会渲染这个空的子路由。
              {
                path:'',
                component:component4
              }
           ]
         }
      ]
  • router 实例方法
    在Vue实例内部,能够经过this.$router获取实例对象,

    • router.push({path:'/user',params:{id:'123'}}) 跳转
    • router.replace(),与上面相同,不会添加新的记录。
    • router.go(-1),表示在路由记录中前进或者后退多少步。
    • 在html <template></template>中,能够经过 {{$route}},获取路由配置的相关信息,从而渲染DOM。好比: 经过

      <template v-for="(items,index) in $router.options.routes">
        <title>
           {{items.name}}
        </title>
      </template>

      就能够将路由配置信息与页面导航栏对应,列表渲染出导航栏,

    • watch方法中监听$route,能够动态配置组件,不一样url复用同一组件

      watch:{
              `$route`:function(to,from) {
                  // 经过to,from 获取url信息
              }
           }
    • 导航守卫(路由钩子)

      经过注册一个全局路由钩子函数,在初始化const router = new VueRouter({})的时候,定义router.beforeEach((to,from,next)=> {...}),在每次进入目标路由以前触发。配合Vuex能够很是方便的进行权限管理

      router.beforeEach((to, from, next) => {
             if (store.getters.getisAuthority) {
               // 检查已经登陆了,就继续跳转。
               next()
             }else if(to.fullPath === "/login"){
                 // 跳转到登陆页面,则清空登陆相关信息
                 clearCookie()
                 next()
             } else {
               next({
                   path:"/login"
               });
          }})

Vue-cli入门

Vue 提供一个 官方命令行工具,可用于快速搭建大型单页面应用

目前已经发布到了V3.0.0-alpha.5

npm install -g @vue/cli
  vue create my-project

基本用法,文档里面也比较清楚,参考这里,
经过下面几步,快速搭建项目基础结构。

# 全局安装 vue-cli
    $ npm install --global vue-cli
    # 建立一个基于 webpack 模板的新项目
    $ vue init webpack my-project
    # 安装依赖,走你
    $ cd my-project
    $ npm install/ yarn
    $ npm run dev

(若是没有使用任何框架的基础上,也想快速搭建一个大型项目的目录结构,能够考虑yeoman快速生成一个新的项目)

基础配置:

  • 修改开发环境下port

    /config/index.js文件中, 手动修改 module.exports = { dev:{ port :8080}}

  • 配置代理

    /config/index.js目录下,(以代理3000端口上数据请求为例)

    dev: {
              proxyTable: {
                '/rest/*':{
                          target:'http://127.0.0.1:3000',
                          secure:false,
                          pathRewrite:{
                             '^/rest':''
                          }
                      }
              },
  • 生产环境关闭sourcemap

    /config/index.js目录下,build:{} 中的productionSourceMap改成false

  • 配置路径别名(alias)

    一般在项目中会看到诸如这样的 import Cookie from "@/util/cookie.js"的引入,@就是vue-cli中默认设置的alias

    /build/webpack.base.conf.js/文件中,resolve对象下添加属性,指向对应的路径

    resolve:{
         alias:{
           'page':path.resolve(__dirname,'../src/page')
         }
      }
  • 区分不一样环境

    • 经过process.env.NODE_ENV值区分

      /build/webpack.dev.conf.js/build/webpack.prod.conf.js中,经过

      new webpack.DefinePlugin({
          'process.env': env
       }),

      建立了编译时能够配置的全局常量,用来区分开发/发布/测试环境,env对应的值,能够在/config/目录下的,*.dev.js文件下配置的。

      而后,在其它业务代码里面,直接使用这个全局变量,好比在 main.js里面:

      if(process.env.NODE_ENV === "development"){
               console.log("开发环境")
           }
    • 经过命令行区分不一样环境

      一样使用上面的方法,添加一个全局变量,不一样的是从命令行中获取参数。
      好比,打包时还区分 发布环境 和 预发环境,就能够修改以下,

      new webpack.DefinePlugin({
         'process.env': env,
         'VERSION':process.argv[2] == "pro"?'"pro"':'"sit"'
      }),

      在命令行中打包时,可使用 npm run build --env sit,在业务代码中,经过全局变量VERSION,一样能够区分不一样环境。

参考连接

相关文章
相关标签/搜索