一个栗子实践 vue2.0 与 1.0 的区别

avatar
全栈开发工程师 @京东

序言

本文通过一个初熟的小栗子来实践2.0与1.0的不同之处,最终的效果如下图所示:

提示

  • 可作为vue2.0+vuex入门参考
  • 只粘贴核心代码,看源码建议直接下载demo 其实只有sass没有贴上啦
  • demo在移动端亲测可用

搭建项目

知识点

这个栗子将涉及以下知识点:

  • vue2.0
  • vue-cli脚手架
  • vuex状态管理库
  • webpack

版本声明

本篇文章的版本:

  • node: v6.2.0
  • vue: v2.1.0
  • vuex: v2.0.0
  • vue-router: v2.1.1
  • webpack: v1.13.2

文件目录

安装

首先请确保已安装了node、npm、webpack。

  1. 安装vue脚手架。

    npm install vue-cli -g
  2. 选择一个目录并执行。

    vue init webpack 项目名字 或者vue init webpack-simple 项目名字 <注意不能用中文>

    然后根据命令行的相关提示输入信息;
    webpack与webpack-simple两者的区别在于webpack-simple没有包括eslint等功能,普通的项目用simple就好了;
    我使用的是前者。

  3. 进入项目目录下载相关依赖。

    cd 项目目录
    npm install
    
  4. 启动。

    npm run dev

这一行命令会自动启动浏览器并运行项目(如果你不想占用8080端口,可通过 项目 /config/index.js 中的port修改)。

初始化

初始化入口js文件 main.js

main.js是应用入口文件,可以在这里

  • 配置路由vue-router
  • 引入路由子组件
  • 引入状态管理store(注入所有子组件)
  • 实例化Vue
  • 引入公共样式等

已完成的main.js如下: 懒癌患者可直接拷贝

import Vue from 'vue'
import store from './store'
//引入路由及组件
import VueRouter from 'vue-router'
import App from './App'
import Home from './components/Home'
import Clocklist from './components/Clocklist'
//引入公共css
import './static/css/reset.css'
Vue.use(VueRouter)
//定义路由
const routes = [
    {
        path : '/',
        component : Home
    },
    {
        path : '/home',
        component : Home
    },
    {
        path : '/clocklist',
        component : Clocklist
    },
]
//创建实例
const router = new VueRouter({
  routes
})
//实例化,并将store、router挂载到根实例,从而应用到整个项目
new Vue({
  store,
  router,
  ...App
}).$mount('#app')//或者直接在options里声明挂载的el

与1.0的不同

  • 映射路由:1.0是通过router的map方法映射路由,并且map接收的是一个对象,2.0中map()被替换了,通过实例化VueRouter并定义一个数组来映射路由;
  • 初始化路由:1.0通过router.start()来初始化路由,2.0中router.start()被替换了,直接通过挂载到vue根实例进行初始化

初始化根组件App.vue

在App.vue中添加路由,并引入Sidebar.vue组件,对应的样式直接写在每个独立的组件下,注意这里使用了sass语法,需在 ./build/webpack.base.conf.js 中配置,如下所示:

vue: {
    loaders: utils.cssLoaders({ sourceMap: useCssSourceMap }),
    postcss: [
      require('autoprefixer')({
        browsers: ['last 2 versions']
      }),
      require('postcss-import'),
      require('postcss-sass-extend'),
      require('postcss-simple-vars'),
      require('postcss-nested')//sass嵌套语法,其他的看最后一个单词就知道是干什么的了
    ]
  }

下面附上整个App.vue的代码,注意看注释掉的部分哦。 从这里开始后面的sass不贴出来了




@import './static/sass/_function.scss';
body {
    overflow: hidden;
}
.clock_header {
    height: 42px;
    line-height: 42px;
    background: #39383e;
    h1 {
        font-size: 18px;
        color: #fff;
        text-align: center;
        &::before {
            content: '';
            width: 18px;
            height: 18px;
            display: inline-block;
            vertical-align: middle;
            margin: -2px 5px 0 5px;
            background: url(./static/img/logo.png) no-repeat;
            background-size: 100% 100%;
        }
    }
}
.clock_nav {
    height: 36px;
    line-height: 36px;
    background: #f0eff5;
    padding-left: 5px;
    font-size: 0;
    a {
        display: inline-block;
        padding: 0 10px;
        position: relative;
        font-size: 14px;
        &.router-link-active {
            color: $color_red;
        }
        &:not(:last-child) {
            &::after {
                content: '';
                width: 1px;
                background: #e1e0e6;
                position: absolute;
                right: 0;
                top: 12px;
                bottom: 12px;
            }
        }
    }
}
.clock_container {
    @extend %clearfix;
}
.clock_sidebar {
    width: 30%;
    float: left;
    box-sizing: border-box;
    border: 1px solid #ddd;
    margin-top: 10px;
}
.clock_router {
    width: 70%;
    float: left;
    position: relative;
    &_inner {
        position: absolute;
        left: 0;
        right: 0;
        top: 0;
        transition: all .3s linear;
    }
}
.slide-left-enter{
  opacity: 0;
  transform: translate3d(60px, 0, 0);
}
.slide-left-leave-active {
  opacity: 0;
  transform: translate3d(-60px, 0);
}

这里我将路由样式设置为相对定位,路由的子组件设置为绝对定位,可以解决切换路由的时候页面抖动问题。

与1.0的不同

  • 根元素:在2.0中template下需要有一个根元素(clock_wrap),否则会报错;
  • 路由导航:在1.0中我们通过v-link来指定导航链接,在2.0中可以直接使用router-link组件来导航,在浏览器中渲染后是一个a标签,并且会自动设置选中的class属性值.router-link-active, 然后通过to 属性指定链接;
  • 过渡:在1.0中通过在目标元素(router-view)使用transition与transition-mode添加过渡,在2.0中,则改成了使用transition标签包裹目标元素,可以自定义name过渡,也可以使用自带的mode添加过渡动效(如mode=”out-in”),2.0中也支持通过$route设置基于路由的动态过渡;
  • 钩子:在1.0中的ready已经被mounted取代,此外2.0还新增了beforeMount、beforeUpdate、update等,下面是1.0与2.0生命周期示意图
1.0

2.0

创建首页Home.vue


通过nowTime ()方法获取当前的时间,doClock ()分别变更状态、时长以及存储计时记录,后面会讲到vuex部分。

与1.0的不同

  • 数据绑定:与1.0一样绑定数据的形式都使用“Mustache” 语法,但2.0不能在html属性中使用了,比如栗子中的绑定id 的方法v-bind:id=”clockId”而不能直接使用{{clockId}},否则会报错;
  • 真实的html:1.0中输出真实的html是使用三个大括号{{{ }}},2.0之后需要使用v-html指令,如上面注释掉的部分所示;

创建侧边栏Sidebar.vue


通过计算属性computed去获取状态与时长。

继续创建打卡列表Clocklist.vue


与1.0的不同

  • v-else-if:在2.0中新增了v-else-if,类似于js中的else if,不能单独使用,需跟在v-if之后;
  • v-for:在使用v-for遍历对象的时候,当存在index时,1.0的参数顺序是(index, value),2.0变成了(value, index);
  • v-for:1.0中,v-for块内有一个隐性的特殊变量$index可以获取当前数组的索引,在2.0中移除了,改为了以上这种显式的定义方式;
  • key:key替代track-by

vuex部分

vuex是为vue.js设计的一个状态管理模式,主要是用来存储共享状态、实现数据通信,简单理解就是统一管理和维护各个vue组件的状态 ,它可以解决多层嵌套组件的传参、兄弟组件的状态传递等难题, 代码更结构化且容易维护。核心概念包括State、Getters、Mutations、Actions、Modules。

创建index.js

在src目录新建store文件夹用来存放共享数据(vuex),然后新建index.js,用来初始化并导出 store。 store已经在main.js中引入

import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import getters from './getters'
import mutations from './mutations'
import actions from './actions'
Vue.use(Vuex)
export default new Vuex.Store({
    state,
    getters,
    mutations,
    actions
    strict: process.env.NODE_ENV !== 'production', //是否开启严格模式
})

strict为是否开启严格模式,在这种模式下任何状态变更不是由Mutation函数触发的都会报错,但是为了避免性能损失,不要在发布环境开启严格模式
在构建大型应用时,store对象会变的非常臃肿,Vuex允许将store分割为模块(module),每个模块有自己个State、Mutations、Actions、Getters。

const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}
const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}
const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

创建state.js

在store目录下继续创建state.js,代码如下

const state = { 
    status: '已结束',
    duration: '0',
    timer: null,
    len: 0,
    clockList: []
}
export default state

Vuex使用一个state包含了全部的应用层级状态–也就是一个单一状态树
通常在计算属性(computed)返回(检测到数据发生变动时就会执行对相应数据有引用的函数),如下:

computed: {
  list () {
        return this.$store.state.status
    }
}

创建mutations.js

在store目录下继续创建mutations.js。

import * as types from './mutation-types'
export default {
    [ types.CHANGE_STATUS ] ( state) {
        if( state.status === '已结束' ) {
            state.status = '已计时'
        }else if(state.status === '已计时') {
            state.status = '已结束'
        } 
    },
    [ types.ADD_DURATION ] ( state, obj ) {
           if( state.status === '已计时' ) {
                state.duration = obj.time
                state.timer = obj.timer
           }else {
                clearInterval(obj.timer)
           }
           
    },
    [ types.SAVE_CLOCK_LIST] (state, nowTime) {
        if( state.status === '已计时' ) {
            console.log(state.clockList.length)
            state.len = state.clockList.length
            state.clockList.push({"gotowork": nowTime, 'gooffwork': ''})
        }
        if( state.status === '已结束' ) {
            state.clockList[state.len].gooffwork = nowTime
        }
    } 
}

mutations是注册各种数据变化的方法,它接受state作为第一个参数,需注意以下几点

  • 变更state必须通过mutation提交,这样使得我们可以方便地跟踪每一个状态的变化
  • mutation 必须是同步函数,异步应在action操作
  • 通常使用常量替代mutation事件类型,在实际操作中通常会建立一个mutation-types.js来存储mutation常量,这样的好处是可以对整个 app 包含的 mutation 一目了然

创建mutation-types.js

在store目录下继续创建mutation-types.js,用来存储mutation事件名

export const CHANGE_STATUS = 'CHANGE_STATUS'
export const ADD_DURATION = 'ADD_DURATION'
export const SAVE_CLOCK_LIST = 'SAVE_CLOCK_LIST'

创建actions.js

action类似autation,与之不同的是:

  • action不能直接变更state,而是提交mutation
  • action可包含异步操作,而mutation不能(严格模式下报错)

Action基本语法如下:

actions: {
  someMethod (context) {
    context.commit('someMethod')
  }
}

action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。
在实践中,通常使用 ES2015 的 参数解构简化代码,如下:

actions: {
  someMethod ({ commit }) {
    commit('someMethod')
  }
}

最后附上actions.js的所有代码

import * as types from './mutation-types'
export default {
    changeStatus({ commit }) {
        commit(types.CHANGE_STATUS)
    },
    addDuration(context) {
        let num = 1, obj = {}
        if(context.state.status === '已计时') {
            obj.timer = setInterval(() => {
                let h = parseInt(num / 3600),
                    m = parseInt(num / 60),
                    s = num
                    if(s >= 60) {
                        s = s % 60
                    }
                    if(m >= 60) {
                        m = m % 60
                    }
                obj.time = h + '时' + m + '分' + s + '秒'
                context.commit(types.ADD_DURATION, obj)
                num ++
            }, 1000)
        }else {
            context.commit(types.ADD_DURATION, obj)
        }
    },
    saveClockList({ commit }, nowTime) {
        commit(types.SAVE_CLOCK_LIST, nowTime)
    }
}

创建getters.js

export default {
   getStatus:state => state.status,
   getDuration:state => state.duration,
   switchTime:state => {
        //转换前
        let date = '',
            toTime = '',
            offTime = '',
            list = []
        //转换后
        let switchDate = '',
            switchToTime = '',
            switchOffTime = ''
        state.clockList.forEach(function (v, i) {
            switchDate = v.gotowork.getFullYear() + '年' + ( v.gotowork.getMonth() + 1 ) + '月' + v.gotowork.getDate()
            switchToTime = v.gotowork.getHours() + ':' + v.gotowork.getMinutes() + ':' + v.gotowork.getSeconds()
            if(v.gooffwork !== '') {
                switchOffTime = v.gooffwork.getHours() + ':' + v.gooffwork.getMinutes() + ':' + v.gooffwork.getSeconds()
            }else {
                switchOffTime = ''
            }
            list.push({'date': switchDate, 'gotowork': switchToTime, 'gooffwork': switchOffTime})
        })
        return list
   }
}

getter接收state作为第一个参数,我们可以通过它

  • 获取state的状态
  • 对需要返回的数据进行处理,如过滤、转换等

关于代码结构

只要遵守了vuex的规则,如何组织代码可根据项目的实际情况以及个人、团队的使用习惯,vuex并不会限制你的代码结构。所以,放开手脚一起搞事吧!

以上就是整篇文章的所有内容,如有错误,恳请指正! 反正咱也不改

写在最后

不得不说前端的技术更新真是快啊,从来没有哪个行业像前端这样繁荣却又令人不安。作为前端人,我们唯有保持对新技术敏锐的嗅觉与热情,才能避免被技术前进的浪潮拍在沙滩上,正所谓路漫漫其修远兮,吾将… 好了好了,搬砖去了…

参考资料

cn.vuejs.org/v2/guide/
vuex.vuejs.org/zh-cn/getti…
juejin.cn/post/684490…
aotu.io/notes/2016/…

vue vue2.0 vue2.0与1.0的区别栗子 vue 感谢您的阅读,本文由 凹凸实验室 版权所有。如若转载,请注明出处:凹凸实验室(aotu.io/notes/2016/…

本文对你有帮助?欢迎扫码加入前端学习小组微信群: