VueJS之-“Vuex篇“(四)

Vuex

Vuex借鉴了Flux和Redux设计思想.

优点:

    Vue组件父子之间通过$on、$emit自定义事件来进行通信

    Vuex可以解决同级组件之间无法传递消息

执行步骤

Store: 数据仓库,包含state对象,组件通过getter从store读取数据,通过Getter

核心模块:

    1、state: 定义存储状态

    2、getter: 对数据进行过滤,获取state数据,在计算属性中获取state的值

    3、mutation: 同步操作,更改 Vuex 的 store 中的state值的唯一方法是提交 mutation

    4、action: 异步操作,Action通过commit()方法来调用mutation中的方法在改变来改变state的值,而不是直接改变state中的值

            分发Action: store.dispatch()方法触发

    5、modules: 如果应用过大,便可以使用 modules 来分割管理,不至于 store 变得非常臃肿


    调用同步的mutation  this.$store.commit('mutationName')

    调用异步的Action    this.$store.dispatch('mutationName')

        export default {
            insertMessage({commit}, insertData){
                fetch('/login',{
                    username,
                    password
                })
                .then((res)=>{
                    return res.json();
                })
                .then((res) => {
                    // 调用mutation
                    commit('insertMessage', res.data);
                })
            }
        }

State

用来存储数据,如果在组件中获取state中的数据,可以通过两种方法来获取: 计算属性computed 和 Getters

getter

在组件中用来获取Store中state,通过getter获取,组件中在通过computed来调用getter

getters.js
export const sideData = (state) => {
    return state.sideData;
} 

<template>
    <div class="box">
        {{ getSideData }}
    </div>
</template>

// 方法1: computed
computed: {
    getSideData () {
        return this.$store.state.sideData;            // 当getSideData的值改变就会显示出来
    }
}


// 方法2: mapState函数获取
import { mapState } from 'vuex';        // 引用mapState()
export default {
    name: 'side',
    computed: {
        ...mapState({    // 对象展开运算符
            getSideData: (state) => {
                return state.sideData
            }
        })
    }
}

Mutations

用来更改Store中state的数据,它是同步的

1、commit()触发Action, 之后Action触发mutation来改变state的值

    // 定义mutations 
    export default {
        changeSideData(state, sideJson){
            state.sideData.push(sideJson)
        }
    }

    // 触发
    methods:{
        addSideData(){
            let side = {
                id: 1,
                name: this.name,
                introduce: this.introduce
            }
            this.$store.commit('changeSideData', side);
        }
    }

2、使用mapMutations() 来定义改变

Actions

actions提交到 Mutations中,action而不能直接改变state的值

actions可以包含异步操作,mutations是同步的

// 定义action
export const holderSilde = ({commit}, {name, introduce}) => {    
    // {name, introduce} 这里注意一定对象传过来,这里为解析赋值写法,函数不接收第三个参数
    var side = {
        id: 1,
        name: name,
        introduce: introduce
    }
    commit('changeSideData', side)
}


// 调用 分发dispatch
methods:{
    addSideData(){
        // 第二个为传过去的参数
        this.$store.dispatch('holderSilde', {name: this.name, introduce: this.introduce});
    }
}

Modules

Vuex可以将Store分割到各模块,每个模块都有自己的store、mutations、actions、getters

const moduleA = {
    state: { ... },
    mutations: { ... },
    actions: { ... },
    getters: { ... }
}

const moduleB = {
    state: { ... },
    mutations: { ... },
    actions: { ... }
}

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

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

示例

// store.js
import Vue from 'vue'
import Vuex from 'vuex'
import mutations from './mutations.js';
import actions from './actions.js';
import getters from './getters.js';
Vue.use(Vuex);

// 初始化数据
const state = {
    currentId: 0,
    currentUsername: '',
    sideData: [
        {
            id: '1',
            username: 'siguang',
            introduce: '用户描述'
        },
        {
            id: '2',
            username: 'lulu',
            introduce: '用户描述'
        }
    ]
}

export default new Vuex.Store({
    state,
    getters,
    actions,
    mutations,
})


// main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store.js'
Vue.config.productionTip = false;

let vm = new Vue({
    el: '#app',
    router,
    store,
    components: { App },
    template: '<App/>'
})

window.vm = vm;

| https://vuex.vuejs.org/zh

VueJS 之 “插件篇“(三)

| vue-router 路由插件
| vue-validator 表单校验插件
| vue-resource ajax插件

vue-cli3 脚手架

一、安装

    $ npm install -g @vue/cli            // 最新版本3.0

    $ vue create hello-world            // 创建项目 vue create --help

        注意 如果安装sass选择手动

        1、> default (babel, eslint)
         > Manually select features

        2、选择预解释器
        >    (*) CSS Pre-processors                // 空格选择

        // 如果安装2.0的模板
        $ vue init webpack my-project        // vue init webpack-simple#1.0 mynewproject

    $ cd hello-world

    $ yarn serve     // 启动服务

    $ yarn build  // 打包

    // 安装插件
    $ vue add router
    $ vue add vuex


二、package.json

    script: {
        "serve": "vue-cli-service serve --mode test231",
    }

    --mode        指定环境模式 (默认值:production)
    --dest        指定输出目录 (默认值:dist)
    --modern      面向现代浏览器带自动回退地构建应用
    --target      app | lib | wc | wc-async (默认值:app)
    --name        库或 Web Components 模式下的名字 (默认值:package.json 中的 "name" 字段或入口文件名)
    --no-clean    在构建项目之前不清除目标目录
    --report      生成 report.html 以帮助分析包内容
    --report-json 生成 report.json 以帮助分析包内容
    --watch       监听文件变化

    环境变量设置
    script: {
        "serve": "FOO=bar vue-cli-service serve --mode test231",
    }

    vue.config.js中获取
    console.log(process.env.FOO);        // bar


三、vue.config.js 配置

    'use strict'
    module.exports = {

        // 修改webpack配置的方式, 该对象会对webpack-merge合并
        configureWebpack: {
          optimization: {
            splitChunks: false
          }
        },
        // 修改 Loader选项
        chainWebpack: config => {
            config
                .plugin('html')
                .tap(args => {
                    // 防止 Cyclic dependency 错误
                    args[0].chunksSortMode = 'none'
                    return args
                })
        },
        // 配置代理
        devServer: {
            // mock
            // 在这里定义mock需要重启server,改为在 utils/mock 中定义
            // https://webpack.js.org/configuration/dev-server/
            before: function (app) {
                app.get('/api/version', function (req, res) {
                    res.json({
                        data: 'V1.0.0',
                        message: '',
                        status: 0,
                    });
                });
            },

            // 反向代理配置
            // https://github.com/chimurai/http-proxy-middleware#proxycontext-config
            proxy: {
                '/api': {
                    target: 'http://172.13.3.232:12080',
                    // ws: true,
                    changeOrigin: true,
                    autoRewrite: true,
                    pathRewrite: {
                        '^/api/': '/'
                    }
                },    
            },
            disableHostCheck: true
        }
    }


四、public目录、css样式

    public目录存放静态资源

    cli支持Sass、Less、Stylus预处理器


二、main.js 入口文件

    import Vue from 'vue'
    import chat from './components/chat.vue';

    new Vue({
        el: '#app',
        render: h => h(chat)         // 调用其它.vue文件必须这么写
    })

    render: h => h(chat) 相当于

    render: (function (h) {  
          return h(App);
    });  


三、不需要代码校验

    在webpack.base.conf.js里面删掉下面:

    preLoaders: [
        {
            test: /\.vue$/,
            loader: 'eslint',
            include: projectRoot,
            exclude: [/node_modules/, /ignore_lib/]
        },
        {
            test: /\.js$/,
            loader: 'eslint',
            include: projectRoot,
            exclude: [/node_modules/, /ignore_lib/]
        }
    ]


https://cli.vuejs.org/zh/

vue-router 路由

一、安装

    $ npm install vue-router --save-dev


二、引用路由:

    import Vue from 'vue'
    import VueRouter from 'vue-router'
    Vue.use(VueRouter)

    const routes = [
        { path: '/foo', component: Foo },        // 一个路由对应一个组件
        { path: '/bar', component: Bar }
    ]
    const route = new Router(    
        mode: 'history',
        routes,
    })

    let vm = new Vue({
        el: '#app',
        route,
        components: { App },
        template: '<App/>'
    })


三、使用路由

    html:

        <div id="app">
            <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
            <router-link to="/foo">Go to Foo</router-link>

            <!-- 加载路由视图 -->
            <router-view></router-view>
        </div>

     router.js

        import Vue from 'vue'
        import VueRouter from 'vue-router'
        import login from './components/login'

        // 将路由插件加载到Vue中
        Vue.use(VueRouter);

        // 路由映射配置
        const routes = [
            { 
                path: '/login', 
                component: login
            },
            {
                path: '/*',
                redirect: '/404'        // 重定向
            }
        ]

        // 创建路由实例
        const router = new VueRouter({
            routes        // 将定义的配置当参数注入路由
        })

        // 创建和挂载根实例
        const app = new Vue({
            router
        }).$mount('#app')


四、动态路由参数

    const router = new VueRouter({
        routes: [
            // 动态路径参数 以冒号开头
            { path: '/user/:id', component: User }
        ]
    })

    // 路由参数  /user/:username     匹配/user/haha    $route.params 来取参数  { username: 'haha' }
    mounted(){
        let usernmae = this.$route.params.username;
    }

    1、router-link 参数

        <router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>

    2、this.$router.push中参数

        router.push({ name: 'user', params: { userId: 123 }})


五、路由跳转

    1、$router.push(location): js内加路由跳转

        // 字符串
        router.push('home')

        // 对象
        router.push({ path: 'home' })

        // 命名的路由
        router.push({ name: 'user', params: { userId: 123 }})

        // 带查询参数,变成 /register?plan=private
        router.push({ path: 'register', query: { plan: 'private' }})

    2、router.replace(): 与push很像,不同的是replace不会向history添加到新记录

    3、router.go(n): 指定向后跳几步,与window.history.go(n)相同,history历史记录跳转

    4、<router-link to="home">home</router-link>            // 编辑出来是<a>标签


五、<router-view> 渲染路由视图组件

    <router-view></router-view>

    支持多个渲染:
    <router-view></router-view>
    <router-view name="a"></router-view>
    <router-view name="b"></router-view>

    const router = new VueRouter({
        routes: [
            {
                path: '/',
                components: {
                    default: Foo,
                    a: Bar,
                    b: Baz
                }
            }
        ]
    })


六、捕获所有路由或 404 Not found路由

    {
        path: '*',
        redirect: function () {
                return '/admin';
        }
    }


七、History模式

    1、hash: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器。

    2、history: 依赖 HTML5 History API 和服务器配置。查看 HTML5 History 模式. (url不会带hash “#“)

    3、abstract: 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式

    export default new VueRouter({
        mode: 'history',
        routes: [...]
    })            


八、路由钩子

    1、beforeEach、afterEach 路由跳转前后的钩子

        var auth = {
            loggedIn: function () {
                return localStorage.getItem(KEY_OF_LOGGEDIN) === 'true'; 
            }
        }

        router.beforeEach((to, from, next) => {
            if (to.matched.some(record => record.meta.requiresAuth)) {
                if (!auth.loggedIn()) {
                    next({
                        path: '/login',
                        query: {redirect: to.fullPath},
                    });
                } else if (auth.requiresAlterPassword()){
                    next({
                        path: '/firstLogin'
                    });
                } else {
                    next();
                }
            } else {
                next();
            }
        });

    2、to: 要进入目录的路由对象

    3、from: 要离开的路由对象

    4、next: function, 用该方法来resolve这个钩子

        next({
            path: '/login',        // 跳转的
            query: {redirect: to.fullPath},                // to.fullPath是当前的路径
        });


九、meta 定义路由的时候可以配置meta字段

    1、设置meta属性,可以通过它来设置不需要判断登录的路由,在beforeEach的时候进行处理

        {
            path: '/firstLogin',
            name: 'firstLogin',
            meta: {
                requiresAuth: false
            },
            component: (resolve) => require(['../views/firstLogin.vue'], resolve),
        }

    2、matched来访问meta属性


十、路由对象

    1、$route.path: 当前路由的路径,总是解析为绝对路径,如 "/foo/bar"

    2、$route.params: 获取动态路由的参数

    3、$route.query:  URL 查询参数, 例如,对于路径 /foo?user=1,则有 $route.query.user == 1

    4、$route.hash: 路由的 hash 值 (带 #) ,如果没有 hash 值,则为空字符串。

    5、$route.matched: 

    6、$route.name: 路由的名称

vue-resource ajax插件

一、它提供了两种方式来处理HTTP请求: 

    使用Vue.http或this.$http

    使用Vue.resource或this.$resource


二、特点: 支持拦截器

    拦截器是全局的,拦截器可以在请求发送前和发送请求后做一些处理。

    拦截器在一些场景下会非常有用,比如请求发送前在headers中设置access_token,或者在请求失败时,提供共通的处理方式。


三、使用方式: 

    1、拦截器  使用inteceptor

    Vue.http.interceptors.push((request, next) => {
        // 请求发送前的处理逻辑
        next((response) => {
            // 请求发送后的处理逻辑
            // 根据请求的状态,response参数会返回给successCallback或errorCallback
            return response
        })
    })


四、请求类型

    get(url, [options])
    head(url, [options])
    delete(url, [options])
    jsonp(url, [options])
    post(url, [body], [options])
    put(url, [body], [options])
    patch(url, [body], [options])

    <script>
        var demo = new Vue({
            el: '#app',
            data: {
                gridColumns: ['customerId', 'companyName', 'contactName', 'phone'],
                gridData: [],
                apiUrl: 'http://211.149.193.19:8080/api/customers'
            },
            ready: function() {
                this.getCustomers();
            },
            methods: {
                getCustomers: function() {
                    // get请求
                    // then方法只提供了successCallback,而省略了errorCallback。
                    // catch方法用于捕捉程序的异常,catch方法和errorCallback是不同的,errorCallback只在响应失败时调用,而catch则是在整个请求到响应过程中,只要程序出错了就会被调用。

                    this.$http.get(this.apiUrl)
                        .then((response) => {
                            // Vue实例方法,设置gridData属性赋值,并触发视图更新
                            this.$set('gridData', response.data)
                        })
                        .catch(function(response) {
                            console.log(response)
                        })
                }
            }
        })
    </script>

5、options对象

    发送请求时的options选项对象包含以下属性: 

    参数             类型                                    描述
    url             string                      请求的URL

    method        string                请求的HTTP方法,例如: 'GET', 'POST'或其他HTTP方法
    body            Object                 FormData string    request body
    params        Object                请求的URL参数对象
    headers        Object                request header
    timeout        number                单位为毫秒的请求超时时间 (0 表示无超时时间)
    before        function(request)    请求发送前的处理函数,类似于jQuery的beforeSend函数
    progress     function(event)      ProgressEvent回调处理函数
    credientials    boolean      表示跨域请求时是否需要使用凭证
    emulateHTTP        boolean      发送PUT, PATCH, DELETE请求时以HTTP POST的方式发送,并设置请求头的X-HTTP-Method-Override
    emulateJSON        boolean      将request body以application/x-www-form-urlencoded content type发送

| https://router.vuejs.org/zh-cn/
| https://vuex.vuejs.org/zh-cn/
| https://github.com/dai-siki/vue-image-crop-upload 头像上传组件
| http://www.cnblogs.com/pandabunny/p/5417938.html // vue引用jquery
| http://router.vuejs.org/zh-cn/installation.html // 路由插件
| http://yuche.github.io/vue-strap/ // vueStrap
| http://bootstrap-table.wenzhixin.net.cn/zh-cn/ // bootstrap Table
| https://github.com/PanJiaChen/vue-element-admin
| https://github.com/opendigg/awesome-github-vue?f=tt&hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io // vue项目汇总

VueJS 之 “组件篇“(二)

| 全局、局部组件
| 数据传递(props、组件通信、slot)
| props、sync双向绑定、once单次绑定
| 组件之间通信,$dispatch() 派发事件、 $broadcast() 广播事件,事件向下传导给所有后代
| slot 内容分发
| 组件是Vue的最强大功能之一,组件可以扩展html元素,封装可复用代码

全局组件

全局组件在任何组件内都可以调用,无需引用或通过component加载

main.js

    // 定义一个全局的组件
    var myComponent = Vue.extend({
        template: '<div> 组件已经渲染出来 </div>'
    })

    // 全局注册
    Vue.component('crm-component', myComponent);

    let vm = new Vue({
        ...
    }}


main.vue 子组件

    <templte>
        <div class="welcome">
            <h1>视图列表页面</h1>
            <crm-component></crm-component>            <!-- 调用 -->
        </div>
    </template>

局部组件

必须使用components对象来引用组件

<div id="appA">
    <User></User>
</div>

<script>
    import User import './user';

    var vmA = new Vue({
        el: "#appA",
        components: {        // 局部的写法
            User
        }
    })
</script>

————— 父与子组件之间的数据传递 —————

props

父组件将数据传给子组件通过props,子组件通过自定义事件$on、$emit来挂参数传递给父组件

一、props

    <div id="app">
        <!-- 这里的两个属性值通过props传到组件中 -->
        <component-parent my-name="siguang" my-age='33'></component-parent>
    </div>

    <script>
        var componentCrm = Vue.extend({
            props: ['myName', 'myAge'],
            template: '<h2>{{ myName }} {{ myAge }}</h2>'
        })

        var vm = new Vue({
            el: '#app',
            components: {
                'component-parent': componentCrm
            }
        })
    </script>

    注意: my-name="siguang" 自定义属性以“-”分割

         props: ['myName', 'myAge']        // props 定义的必须以驼峰形势


二、props动态值

    parent.vue:

        <div id="app">
            <child-component :user-name="uname"></child-component>
        </div>

        <script>
            import childComponent from 'childComponent.vue';

            export default{
                data(): {
                    return{
                        uname: 'siguang'
                    }
                },
                components:{
                    childComponent
                }
            }
        </script>


    childComponent.vue:

        <div>
            {{ userName }}        <!-- siguang -->
        </div>

        <script>
            export default{
                propos: ['userName'],
            }
        </script>


三、once 将渲染的结果缓存

    不过当组件中包含大量静态内容时,可以考虑使用 v-once 将渲染结果缓存起来

    <div id="app">
        <component-parent></component-parent>
    </div>

    <script>

        var childrenComponent = Vue.extend({
            template: '<div v-once>'+
                '<h3>这里是子组件的内容: {{ myMsg }}</3>'+
                '<p>这里输入不会影响父组件: <input type="text" v-model="myMsg" /></p>'+
                '</div>',
            data: function(){
                return {
                    myMsg: ''
                }
            },
            props: ['myMsg']
        })

        var parentComponent = Vue.extend({
            template: '<div v-once>'+
                '<p><input text="text" v-model="msg"></p>'+
                '<p>父组件的值 {{ msg }}</p>'+
                '</div>'+
                '<childen-component v-bind:my-msg.once="msg"></childen-component>',
            data: function(){
                return {
                    msg: '这里值只传给子组件一次'
                }
            },
            components: {
                'childen-component': childrenComponent
            }
        })

        var vm = new Vue({
            el: '#app',
            components: {
                'component-parent': parentComponent
            }
        })
    </script>

组件之间通信

props是单向数据流父向子组件传递数据,子向父组件传递可以通过自定义事件来处理

$on(eventName) 监听事件    、$emit(eventName) 触发事件

一、子向父组件发消息

    parent.vue: 

        <template>
            <div class="login">
                <div class="parent-box">
                    <p>显示子组件数据: {{ childrenText }}</p>
                </div>
                <children-component ref="childrenRef" v-on:showChildrenVal="childrenVal"></children-component>
            </div>
        </template>

        <script>
            import ChildrenComponent from './children.vue'
            export default{
                data(){
                    return {
                        childrenText: ''
                    }
                },
                methods:{
                    childrenVal(val){
                        this.childrenText = val;
                    }
                },
                components:{
                    ChildrenComponent
                }
            }
        </script>

    children.vue:

        <template>
            <div class="children" style="padding-top: 50px; border-top: 1px #ccc solid">
                <p><input type="text" v-model="childrenInp" id=""></p>
                <p><input type="button" @click="toParentData" value="提交到父组件中"></p>
            </div>
        </template>

        <script>
            export default{
                data(){
                    return {
                        childrenInp: ''
                    }
                },
                methods:{
                    toParentData(){
                        this.$emit("showChildrenVal", this.childrenInp);
                    }
                }
            }
        </script>


二、父向子发消息

    parent.vue:

        <template>
            <div class="login">
                <div class="parent-box">
                    <p><input type="text" name="" v-model="inputParent" ><br></p>
                    <p><input type="button" value="值传给父组件" @click="sendChildren"><br></p>
                    <p>显示子组件数据: {{ childrenText }}</p>
                </div>

                <children-component ref="childrenRef"></children-component>
            </div>
        </template>

        <script>
            import ChildrenComponent from './children.vue'
            export default{
                data(){
                    return {
                        inputParent: '',
                        childrenText: ''
                    }
                },
                methods:{
                    sendChildren(){
                        this.$refs.childrenRef.$emit('showText', this.inputParent);        // 也可以用props
                    }
                },
                components:{
                    ChildrenComponent
                }
            }
        </script>


    children.vue:

        <template>
            <div class="children" style="padding-top: 50px; border-top: 1px #ccc solid">
                这里是父组件传递的值: {{transmitVal}}
            </div>
        </template>

        <script>
            export default{
                created(){
                    this.$on('showText', function(val){
                        this.transmitVal = val;
                    })
                },
                data(){
                    return {
                        transmitVal: ''
                    }
                }
            }
        </script>


三、v-ref 直接访问到组件

    <div id="app">
        <span ref="msg"> hello </span>
        <span ref="other-msg"> world </span>
    </div>

    <script>
        var vm = new Vue({
            el: "#app",
            data: {
                someProp: 'idName',
                otherProp: 'prop'
            }
        })

        console.log(vm.$refs.msg.textContent);        // hello
        console.log(vm.$refs.otherMsg.textContent);    // world
    </script>

slot 分发内容

parent.vue:
    <template>
        <div class="login">
            <children-component>
                <h1 slot="header">这里可能是一个页面标题</h1>
                <div class="tag">
                    这里是没有定义slot名称走的默认的slot
                    <div class="tag-til">标题</div>
                    <div class="tag-content">内容部分</div>
                </div>
            </children-component>
        </div>
    </template>

children.vue:
    <template>
        <div class="children">
            <slot name="header"></slot>
            <slot></slot>
        </div>
    </template>

组件化的css

可以通过scoped属性来控制样式是否是只在当前组件下使用

<style>
    /* 全局下的样式 */
</style>

<style scoped>
    /* 当前组件下使用的样式 */
</style>

VueJS 之 “基础篇“(一)

| style与class
| computed、directive、filter
| watch 侦听Data数据的变化
| 生命周期: beforeCreate、create、beforeMount、mounted、beforeUpdate、updated、destroyed
| this.$refs、$el、$data、$watch
| v-bind缩写:、v-on缩写@

Vue2.0

一、Vue的MVVM实现的原理

    ViewModel是Vue.js的核心,Vue实例是作用于某一个HTML元素上的,这个元素可以是HTML的body元素,也可以是指定了id的某个元素

    DOM Listeners 和 Data Bindings看作两个工具,它们是实现双向绑定的关键。

    从view层来看,ViewModel中的DOM Listeners工具会帮我们监测页面上DOM元素的变化,如果有变化,则更改Model中的数据;

    从Model层来看,当我们更新Model中的数据时,Data Bindings工具会帮我们更新页面中的DOM元素。


二、安装

    $ npm i vue --save-dev

    $ npm i vue-cli 脚手架

生命周期

一、构造器

    1、每个构造器都是通过创建一个Vue的实例启动的

        var vm = new Vue({
            // ...    
        })

    2、扩展构造器

        通过扩展构造器可以创建可复用的组件构造器

        var MyComponent = Vue.extend({
            // 扩展选项
        })


二、生命周期

    1、beforeCreate(): 实例初始化,数据观测(data observer)和事件(event/watcher)配置之前被调用

    2、created(): 实例创建完后被调用,完成了数据观测(data observer)和事件(event/watcher)回调,但还挂载还没开始$el属性不可见

    3、beforeMount(): 在挂载开始之前调用,render()函数被调用

    4、mounted(): el被创建的vm.$el替换,挂载到实例上调用钩子

    5、beforeUpdate(): 数据更新时调用,在虚拟DOM重新渲染

    6、updated(): 数据更改导致虚拟DOM重新渲染

    7、destroyed(): 卸载组件

数据绑定

一、文本绑定:  <p>{{ data.name }}</p>

二、html绑定:  <p v-html="data"></p>

三、绑定属性的值 

    <div id="item-{{id}}"></div>

    <div :id="userId"></div>

四、表达式:  {{ dat.num == 1 }}、 {{ num ? 'yes' : 'no' }}、 {{if(ok) {return message} }}

五、过滤器:  {{ message | capitalize }}

    连续过滤器:  {{ message | filterA | filterB }}

常用指令

指令: 以 v- 开头

    <div v-if="greeting == 1"></div>

    带参数: <a v-bind:href="url"></a>

一、v-if、v-else-if、v-else条件渲染:

    <div v-if="type === 'A'">A</div>
    <div v-else-if="type === 'B'">B</div>
    <div v-else>Not A/B/C</div>

二、v-show: 显示还是隐藏元素,用的display

三、v-for 循环

    $index: 索引        $key: 

    * 数组
        <li v-for="item in items" :key="item">{{ item.message }}</li>

    * 数组对象
        <li v-for="(val, index) in objList" :key="index">
            {{index}}. {{key}} : {{val.type}}
        </li>

        ** 注意:key,如果key没有改变不会重新渲染组件,问题在于子组件中存在于绑定

    * filterBy()、orderBy()过滤器

四、v-model: 绑定值,这也是MVVM的关键点

    可以对文本表单、单选框 、多选框、下拉菜单进行绑定值

        1、lazy: 不是输入时就进行同步而是在change的时候才对数据进行同步

            <input type="text" v-model="msg" lazy /> {{msg}}

        2、number: 将用户输入的转成Number型,如果返回NaN那就返回原值

        3、debounce: 设置一个最小延时,否则如果输入是ajax请求,每次敲击都会去请求

            <input type="text" debounce="500" v-model="msg">        // 按完键500毫秒后在进行

    获取checkbox值:

        <input type="checkbox" v-model="toggle" v-bind:true-value="a" v-bind:false-value="b">

        // 当选中时
        vm.toggle === vm.a

        // 当没有选中时
        vm.toggle === vm.b

五、v-bind: 绑定到HTML元素上,v-bind:class="active",缩写 ":" <a :href='url'></a>

六、v-on: 监听DOM事件 v-on缩写: "@" <div @click="doSomeing"></div>

七、ref: 访问子组件

    <div ref="box">xxxx</div>
    <el-button type="primary" slot="append" @click="getRefs">发送</el-button>
    </div>

    {
        methods:{
            getRefs(){
                console.log(this.$refs.box.innerHTML);        // 获取 $refs
            }
        }
    }

    如果绑定一个元素使用 :ref='item'
    <p v-for="item in items" :ref="item"></p>

八、v-pre: 跳过这个元素和它的子元素的编译过程,这样加快了编译的速度

    <div v-pre>{{ 这里不用框架来编译 }}</div>

九、v-cloak:

十、v-once: 只渲染元素和组件一次,之后的渲染都会跳过次元素

    <span v-once>xxxxx</span>

过滤器

过滤器,对绑定值进行处理

一、自定义过滤器

    在一个组件中定义过滤器:
        <span v-text="message | reverse 'addText' "></span>
        export default {
            filters: {
                capitalize: function (value) {
                    value = value.toString();
                    return value.charAt(0).toUpperCase() + value.slice(1)
                }
            }
        }

    定义全局过滤器:
        filters.js
        Vue.filter('datetime', function (value, format) {
            if (!value) {
                return 
            }
            return moment(value).format(format || 'YYYY-MM-DD HH:mm:ss');
        });

        main.js
        import './filters.js'        // 引用


二、内置过滤器

    1、uppercase: 字母转成大写

    2、lowercase: 字母转成小写

    3、capitalize: 首字母转成大写

    4、filterBy: 过滤内容

    5、limitBy: 过滤数据从开始到指定位置结束的内容

    6、orderBy: 过滤排序

    7、currency: 货币过滤

    8、debounce: 延迟过滤


三、双向过滤器

    模型的值显示在视图之前转换它

    <input v-mode="money | formatMoney">
    <div>{{ money | formatMoney }}</div>

    Vue.filter('formatMoney', {

        // model -> vlew过程  在更新<input> 元素之初化值
        read: function(val){
            return '$' + val.toFixed(2);
        },

        // vlew -> model过程 在写回数据之前格式化值
        write: function(val){
            var number = +val.replace(/[^\d.]/g, '');
            return isNaN(number) ? 0 : parseFloat(number.toFixed(2));
        }
    })


四、动态参数

    参数用空格格开
    <input type="text" v-model="userInput">
    <span>{{msg | concat userInput}}</span>

    Vue.filter('concat', function (value, input) {
        // `input` === `this.userInput`
        return value + input;
    })


五、filterBy过滤器

    <div id="app">
        <div>过滤数据里包含 蛋糕 字符 </div>
        <div class="box">
            <ul>
                <li v-for="user in users | filterBy '蛋糕' ">{{ user.name }}</li>
            </ul>
        </div>

        <div style="margin-top: 30px" >过滤出输入的字符</div>
        <input type="text" name="" v-model="searchKey" />
        <div class="box">
            <ul>
                <li v-for="user in users | filterBy searchKey in 'name'">{{ user.name }}</li>
            </ul>
        </div>
    </div>

    <script src="../../js/vue.js"></script>
    <script type="text/javascript">
        var vm = new Vue({
            el: '#app',
            data: {
                searchKey: '',
                users:[
                    {
                        name: '外卖',
                        tag: 1
                    },
                    {
                        name: '鲜花',
                        tag: 5
                    },
                    {
                        name: '蛋糕',
                        tag: 4
                    },
                    {
                        name: '水果',
                        tag: 2
                    },
                    {
                        name: '茶点',
                        tag: 3
                    }
                ]
            }
        })
    </script>


六、orderBy排序

    <div id="app">
        <input type="text" name="" v-model="searchKey" />
        <div class="box">
            <ul>
                <li v-for="user in users | orderBy 'tag'">{{ user.name }}</li>
            </ul>
        </div>
    </div>

    <script type="text/javascript">
        var vm = new Vue({
            el: '#app',
            data: {
                searchKey: '',
                users:[
                    {
                        name: '外卖',
                        tag: 1
                    },
                    {
                        name: '咖啡',
                        tag: 5
                    },
                    {
                        name: '餐厅订座',
                        tag: 4
                    },
                    {
                        name: '蛋糕',
                        tag: 2
                    },
                    {
                        name: '票务',
                        tag: 3
                    }
                ]
            }
        })
    </script>

指令

一、钩子函数

    el:指令所绑定的元素,可以用来直接操作 DOM 。
    binding:一个对象,包含以下属性:
    name:指令名,不包括 v- 前缀。
    value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。
    oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
    expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。
    arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。
    modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
    vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
    oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

二、对象字面量

    <div v-demoA="{ color: 'white', text: 'hello'}"></div>

    Vue.directive('demoA', function(el, value){
        console.log(value.color);
        console.log(value.text);
    })

三、元素指令,相当于angular的E指令

    Vue.elementDirective('my-directive', {
        bind: function(){
            // 操作this.el
        }
    })

    <my-directive></my-directive>            // 不能这么写<div v-my-directive></div>

四、高级选项

    params: 自动提取绑定元素的这些特性.

    <div v-example a="hi"></div>
    Vue.directive('example', {
        params: ['a'],
        bind: function () {
            console.log(this.params.a);         // -> "hi"
        }
    })

computed 计算属性

计算属性当依赖属性发生变化时,属性的值会自动更新,相关的DOM也会同步更新

一、计算属性的getter

    <div id="app">
        <p>user_inof={{ userInfo }}</p>
    </div>

    <script>
        var vm = new Vue({
            el: '#app',
            data: {
                "user_name": "lulu",
                "user_age": "20"
            },
            computed: {
                // 一个计算属性getter
                userInfo: function(){
                    return '姓名: '+ this.user_name +' 年龄: '+ this.user_age;
                }
            }
        })
    </script>

二、计算属性的setter

    var vm = new Vue({
        el: '#app',
        data: {
            "user_name": "lulu",
            "user_age": "20"
        },
        computed: {
            userInfo: function(){
                get: function(){
                    return '姓名: '+ this.user_name +' 年龄: '+ this.user_age;
                },
                set: function(newValue){        // 这里当
                    this.userInfo = newValue;
                }
            }
        }
    })

watch 侦听器

watch 侦听data下指定数据的变化 

watch:{
    message: function(newValue){
        return newValue + 1;        // 如果message变化值加1
    }
}

// 写在实例化下侦听数据变化
// vm.$watch('message', function(newVal, oldVal){
//    console.log(newVal, oldVal);
//    this.message = newVal.replace(/s|g/g, '*');
// })

class和style的绑定

一、class

    // 对象
    <div :class="{ 'active': isActive }"></div>
    var vm = new Vue({
        el: '#app',
        data: {
            isActive: true
        }
    })

    // 数组
    <div :class="[activeClass, errorClass]"></div>        // 三元 <div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
    data: {
        activeClass: 'active',
        errorClass: 'text-danger'
    }    

二、style

    <div :style="[crm, hk]"> 或 <div :style="{color: activeColor, fontSize: fontSize+'px'}">

    var vm = new Vue({
        el: '#app',
        data: {
            activeColor: 'red',
            fontSize: 30,
            crm: {
                color: '#000',
                fontSize: '16px'
            },
            hk: {
                color: '#fff',
                fontSize: '18px'
            }
        }
    })

事件

一、@click

    <div @click="say"></div>
    <div @click="say('hi')"></div>
    <div @click="content + 1"></div>

    <!-- 阻止单击事件冒泡 -->
    <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>

    <!-- 只当事件在该元素本身(而不是子元素)触发时触发回调 -->
    <div v-on:click.self="doThat">...</div>

二、按键别名

    .enter、.tab、.delete (捕获 “删除” 和 “退格” 键)、.esc、.space、.up、.down、.left、.right、.ctrl、.alt、.shift、.meta

三、once: 只处理一次点击  <a v-on:click.once="doThis"></a>

四、修饰符 <input v-on:keyup.13="submit">

模板语法

Vue在底层将模板编译成虚拟DOM渲染函数,也可以不用模板直接写render函数,可选JSX语法

常见问题

一、props传值子元素不会接收

    问题1: 封装tab标签来加载不同子组件,当同一组件重新被打开时,应先消毁之前的相同组件在加载新的组件,但在子组件中destroyed()来侦听消毁缺没有回调

     // 打开新页面
    openTabPage(tabPage, cb) {
        // 先关闭存在的同名的,再打开
        if (this.pages.some(item => item.name === tabPage.name)) {
            this.handleRemove(tabPage.name);
        }
        this.$nextTick(()=>{                // 使用$nextTick 进行重新渲染
            this.pages.push(tabPage);
            this.activeName = tabPage.name;
        })

        if (cb && typeof cb === 'function') {
            cb(this.activeName)
        }

        if (this.tabCreate) {
            this.tabCreate(tabPage)
        }
    },

    问题2: 这种加了一层会对相同子组件进行消毁在重建

        <template slot="tabcreate">
    <create-data-config ref="createDataConfig" :create-prop="createProp"></create-data-config>
  </template>

        openTab(tabName, row){
            // this.createProp['detailInfo'] = row || '';            这种写法子组件watch不到createProp的变化 

            this.createProp = {
                detailInfo: row || ''
            }
  }

        子组件
        watch:{
            createProp: function(){
                debugger;
            }
        }


二、v-bind 内联字符串连接

    data(){
        return {
            addString: '加入内容'
        }
    }

    1.x版本 写法  <div attr="xxxx{{addString}}">
    2.x版本 写法  <div v-bind="'xxxx'+ addString">


三、checkbox动态创建绑定

    // v-model="item.check" 绑定的值要相同
    <p v-for="item in checkList">
        <input type="checkbox" name="cb" v-model="item.check" /> {{item.name}}
    </p>

    data(){
        return{
            checkList:[
                {check: false, name: 'aa'},
                {check: false, name: 'bb'},
                {check: false, name: 'cc'}
            ]
        }
    }


四、vue使用jxs语法

    new Vue({
        el: '#demo',
        render (h) {
            return (    // 使用这种jsx语法
                <AnchoredHeading level={1}>
                    <span>Hello</span> world!
                </AnchoredHeading>
            )
        }
    })

    安装插件
    $ npm install babel-plugin-syntax-jsx babel-plugin-transform-vue-jsx babel-helper-vue-jsx-merge-props babel-preset-en   --save-dev

    .babelrc:
        {
            "presets": ["env"],
            "plugins": ["transform-vue-jsx"]
        }

    https://github.com/vuejs/babel-plugin-transform-vue-jsx


五、vue键盘事件不起作用

    @keyup.enter.native="login"   // 如果不加.native是不起作用有


六、$nextTick 页面某一块数据渲染完成后在执行其它操作

    queryBindBank(){
        let that = this;

        common.ajax({
            url: '/business/web/v1/paytool/payCardList',
            method: 'post',
            data: {},
            dataType: 'json',
            success: function(res) {
                that.showLoading = false;
                if (res && res.data) {

                    // 渲染数据表
                    that.bindBankData = res.data.list;

                    // 需要数据表渲染完后在执行其它操作
                    that.$nextTick(function(){
                        cardList.forEach(function(item, idx, arr){                    
                            if(item.designAbled){
                                that.$refs.multipleTable.toggleRowSelection(item);
                            }
                        })
                    })

                    that.bindBankData.forEach((bank, idx, arr) => {
                        if(bank.designAbled){
                            that.selectedRadio = bank.bankCard;
                        }
                    })
                }
      }
        });
    }

| http://www.jianshu.com/p/95b2caf7e0da // vue-cli
| http://cn.vuejs.org/guide/ // Vue中文官网
| https://github.com/vuejs/vue // github
| http://vuefe.cn/guide/installation.html // 2.0
| http://router.vuejs.org/zh-cn/ // vue-router2.0
| https://github.com/vuejs // Vue组件列表
| http://www.cnblogs.com/dh-dh/p/5606596.html // binding原理,vue的数据
| https://github.com/vuejs/awesome-vue#libraries--plugins
| http://vuex.vuejs.org/zh-cn/ // Vuex 2.0文档
| https://github.com/vuejs/vuex/blob/1.0/docs/zh-cn/intro.md
| http://www.cnblogs.com/keepfool/category/845804.html
| http://www.jianshu.com/p/f8e21d87a572
| https://github.com/pablohpsilva/vuejs-component-style-guide/blob/master/README-CN.md#%E5%9F%BA%E4%BA%8E%E6%A8%A1%E5%9D%97%E5%BC%80%E5%8F%91 // Vue代码规范
| https://juejin.im/book/5a36661851882538e2259c0f/section/5a37bbb35188257d167a4d64 源码