Vue2.x學習筆記(二)

1、vue-router 的使用

vue-router官方文檔javascript

路由實現方式:css

  1. 傳統開發方式 URL 改變後,馬上發生請求響應整個頁面,有可能資源過多致使頁面出現白屏。
  2. 單頁面應用 SPA (Single Page Application),錨點改變後,不會馬上發送請求,而是在某個合適的時機,發起 Ajax 請求,頁面局部渲染。

1. vue-router 實現原理的簡單實現

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue-router</title>
</head>
<body>
    <a href="#/login">登陸</a>
    <a href="#/register">註冊</a>
    <div id="app"></div>
    <script type="text/javascript" src="../node_modules/vue/dist/vue.js"></script>
    <script type="text/javascript">
        var objDiv = document.getElementById('app');

        window.onhashchange = function() {
            console.log(location.hash);
            switch(location.hash) {
                case '#/login':
                    objDiv.innerHTML = '<h2>我是登陸頁面</h2>'
                    break;
                case '#/register':
                    objDiv.innerHTML = '<h2>我是註冊頁面</h2>'
                    break;
                default:
                    objDiv.innerHTML = '<h2>未找到頁面</h2>'
                    break
            }
        }
    </script>
</body>
</html>

2. vue-router 的基本使用

VueRouter 引入以後,Vue 實例會自動掛載$router、$route 兩個屬性對象,組件會繼承 Vue 實例上的這兩個對象。
經過這兩個對象,能夠在組件內部得到路由相關的屬性。$router就是VueRouter對象;經過$route.params能夠獲取動態路由參數;經過$route.query能夠獲取 URL 參數。html

當 Vue 不是全局對象(如使用腳手架工具進行開發)時,須要將 VueRouter 對象掛載到 Vue 對象上,而後才能使用 VueRouter。前端

vue-router的基本使用示例:vue

Vue.use(VueRouter);  // 當Vue不是全局對象時,須要將VueRouter掛載到Vue對象上

let Login = {
    template: "<div>我是登陸頁面</div>"
};
let Register = {
    template: "<div>我是註冊頁面</div>"
};

// 建立router對象
var router = new VueRouter({
    // 配置路由對象
    routes: [
        // 路由匹配規則
        {
            path: "/login",
            component: Login
        },
        {
            path: "/register",
            component: Register
        }
    ]
});

let App = {
    template: `
        <div>
            <router-link to='/login'>登陸頁面</router-link>
            <router-link to="/register">註冊頁面</router-link>
            <router-view></router-view>
        </div>
    `
};

new Vue({
    el: "#app",
    components: {App},
    router: router,  // 將router路由對象交給Vue實例管理
    template: "<App/>"
})
  • 引入 vue-router 模塊後,會有兩個全局的組件 router-link、router-view
    • router-link 組件至關於<a>標籤,它的to屬性至關於<a>標籤的href屬性;
    • router-view 組件是路由匹配組件的出口。

3. 命名路由

命名路由就是給路由規則添加name屬性,而後將 router-link 組件的to屬性改成v-bind屬性綁定。java

Vue.use(VueRouter);

let Login = {
    template: "<div>我是登陸頁面</div>"
};
let Register = {
    template: "<div>我是註冊頁面</div>"
};

// 建立router對象
var router = new VueRouter({
    routes: [
        {
            name: "login",  // 路由命名
            path: "/login",
            component: Login
        },
        {
            name: "register",  // 路由命名
            path: "/register",
            component: Register
        }
    ]
});

let App = {
    template: `
        <div>
            <router-link :to="{name: 'login'}">登陸頁面</router-link>
            <router-link :to="{name: 'register'}">註冊頁面</router-link>
            <router-view></router-view>
        </div>
    `
};

new Vue({
    el: "#app",
    components: {App},
    router: router,  // 將router路由對象交給Vue實例管理
    template: "<App/>"
})

4. 路由參數

路由參數包括:1. 動態路由參數(以冒號標註的參數);2. URL參數(http://xxx.html/?a=1&b=2)。node

let UserParams = {
    template: "<div>動態路由參數頁面</div>",
    created() {
        // VueRouter引入以後,Vue實例上會掛載有$router、$route兩個屬性對象,
        // 組件會繼承Vue實例上的$router、$route對象;經過這兩個對象,能夠在組件內部得到路由參數。
        console.log(this.$router);
        console.log(this.$route.params)
    }
};
let UserQuery = {
    template: "<div>URL參數頁面</div>"
};

// 建立router對象
var router = new VueRouter({
    routes: [
        {
            name: "UserParams",
            path: "/user/:id",  // 動態路由參數,以冒號開頭
            component: UserParams
        },
        {
            name: "UserQuery",
            path: "/UserQuery",
            component: UserQuery
        }
    ]
});

let App = {
    // 兩種路由參數傳入 router-link 的示例:
    // 動態路由參數經過 params 屬性選項傳入參數;URL參數經過 query 屬性選項傳入參數。
    template: `
        <div>
            <router-link :to="{name: 'UserParams', params: {id: 2}}">動態路由參數</router-link>
            <router-link :to="{name: 'UserQuery', query: {userid: 3}}">URL參數</router-link>
            <router-view></router-view>
        </div>
    `
};

new Vue({
    el: "#app",
    components: {
        App
    },
    router: router,
    template: "<App/>"
})

當使用路由參數時,例如從/user/foo導航到/user/bar,原來的組件實例會被複用。由於兩個路由都渲染同一個組件,比起銷燬再建立,複用則顯得更加高效。不過,這也意味着組件的生命週期鉤子不會再被調用react

若是在複用組件時,想對路由參數的變化做出響應的話,能夠簡單地 watch (監測變化) $route 對象:webpack

const User = {
  template: '...',
  watch: {
    '$route' (to, from) {
      // 對路由變化做出響應...
    }
  }
}

或者使用 2.2 中引入的 beforeRouteUpdate 導航守衛:ios

const User = {
  template: '...',
  beforeRouteUpdate (to, from, next) {
    // react to route changes...
    // don't forget to call next()
  }
}

5. 路由組件傳參

經過路由組件傳參,能夠實現同一組件根據參數的不一樣顯示不一樣的內容,達到組件的複用。詳見vue-router官方網站

6. 嵌套路由

當子路由是不一樣的頁面結構時,能夠經過嵌套路由來根據路由加載不一樣的組件。定義子路由:在路由對象router中添加children屬性。

let Song = {
    template: "<div>歌曲內容頁</div>"
};

let Movie = {
    template: "<div>影視內容頁</div>"
};

let Home = {
    template: `
        <div>
            首頁內容
            <br />
            <router-link :to="{name: 'song'}">歌曲</router-link>
            <router-link :to="{name: 'movie'}">影視</router-link>
            <router-view></router-view>
        </div>
    `
};

// 建立router對象
var router = new VueRouter({
    routes: [
        {
            name: "home",
            path: "/home",
            component: Home,
            children: [
                {
                    name: 'song',
                    path: 'song',
                    component: Song
                },
                {
                    name: 'movie',
                    path: 'movie',
                    component: Movie
                }
            ]
        }
    ]
});

let App = {
    // 兩種路由參數傳入 router-link 的示例
    template: `
        <div>
            <router-link :to="{name: 'home'}">首頁</router-link>
            <router-view></router-view>
        </div>
    `
};

new Vue({
    el: "#app",
    components: {
        App
    },
    router: router,
    template: "<App/>"
})

7. keep-alive 在路由中的使用

內置組件keep-alive能夠將組件的狀態緩存,當路由切換後能夠保持路由時加載的組件的狀態。

let Timeline = {
        template: "<div><h3>這是首頁組件</h3></div>",
        created() { console.log('首頁組件建立了'); },
        mounted() { console.log('首頁組件DOM加載了'); },
        destroyed() { console.log('首頁組件銷燬了'); }
    };
    let Pins = {
        template: "<div><h3 @click="clickHandler">這是沸點組件</h3></div>",
        methods: {
          clickHandler(e) { e.target.style.color = 'red'; }
        },
        created() { console.log('沸點組件建立了'); },
        mounted() { console.log('沸點組件DOM加載了'); },
        destroyed() { console.log('沸點組件銷燬了'); }
    };

    let router = new VueRouter({
        routes: [
            {
                path: '/timeline',
                component: Timeline
            },
            {
                path: '/pins',
                component: Pins
            }
        ]
    });

    let App = {
        template: `
            <div>
                <router-link to="/timeline">首頁</router-link>
                <router-link to="/pins">沸點</router-link>
                <keep-alive>
                    <router-view></router-view>
                </keep-alive>
            </div>
        `
    };

    new Vue({
        el: "#app",
        router,
        components: {App},
        template: "<App/>"
    })

8. 在路由中經過 meta 進行權限控制

示例代碼知識點總結:

  • 能夠將某些數據保存到本地的 localStorage 中。
  • 在方法中,能夠經過編程式路由跳轉到指定頁面。
  • 給路由設置meta屬性,以規定該路由是否須要權限驗證。
  • 在全局前置導航守衛中執行路由權限驗證的邏輯。
  • 必須調用全局前置導航守衛中的next()方法,不然頁面不會跳轉。
let Home = {template: "<div>這是首頁</div>"};
let Blog = {template: "<div>這是博客</div>"};
let Login = {
    data() {
        return {name: "", passwd: ""}
    },
    template: `
        <div>
            <input type="text" v-model="name">
            <input type="password" v-model="passwd">
            <input type="button" value="登陸" @click="loginHandler">
        </div>
    `,
    methods: {
        loginHandler() {
            // 將數據保存到本地的 localStorage 中,以模擬登陸
            localStorage.setItem("user", {name: this.name, passwd: this.passwd});
            // 經過編程式導航跳轉到目標頁面
            this.$router.push({
                name: "blog"
            })
        }
    }
};

const router = new VueRouter({
    routes: [
        {
            path: "/",
            redirect: "/home"
        },
        {
            path: "/home",
            component: Home
        },
        {
            path: "/blog",
            name: "blog",
            component: Blog,
            // 給路由作權限控制
            meta: {
                // 規定這個路由是否須要登陸
                authValidate: true
            }
        },
        {
            path: "/login",
            name: "login",
            component: Login
        }
    ]
});
router.beforeEach((to, from, next) => {
    console.log(to);
    console.log(from);
    // 經過目的路由的meta屬性來判斷組件是否設定了權限驗證
    if (to.meta.authValidate) {  // 路由到有登陸驗證的組件時執行
        if (localStorage.getItem("user")) {  // 判斷是否已經登陸,若已登陸,則直接放行
            next();
        } else {
            next({  // 若未登陸,則跳轉到登陸頁面
                path: '/login'
            });
        }
    } else {  // 路由到沒有登陸驗證的組件時執行
        if (localStorage.getItem("user")) {
            if (to.name === "login") {
                console.log(to.name);
                next({
                    path: "/home"
                })
            }else {
                next();
            }
        } else {
            next();
        }
    }
});

new Vue({
    el: "#app",
    router,
    template: `
        <div>
            <router-link to="/home">首頁</router-link>
            <router-link to="/blog">個人博客</router-link>
            <router-link to="/login">登陸</router-link>
            <a href="javascript: void(0)">退出</a>
            <router-view></router-view>
        </div>
    `
})

9. 導航完成以後異步獲取數據

需求:在導航完成以後加載數據,渲染DOM

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue-router在導航完成後獲取數據</title>
</head>
<body>
<div id="app"></div>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="node_modules/vue-router/dist/vue-router.js"></script>
<script src="node_modules/axios/dist/axios.js"></script>
<script>
    // 導航完成後獲取數據,這讓咱們有機會在數據獲取期間展現一個loading狀態,還能夠在不一樣視圖間展現不一樣的loading狀態。
    var Index = {
        template: "<div>我是首頁</div>"
    };
    var Post = {
        data() {
            return {
                loading: false,
                error: null,
                post: null
            }
        },
        template: `
            <div>
                <div class="loading" v-if="loading">
                    loading ...
                </div>
                <div class="error" v-if="error">
                    {{error}}
                </div>
                <div class="content" v-if="post">
                    <h2>{{post.title}}</h2>
                    <p>{{post.body}}</p>
                </div>
            </div>
        `,
        created() {
            // 組件建立完成後獲取數據,此時data已經被監聽了
            this.fetchData();
        },
        // watch: {
        //     "$route": 'fetchData'
        // },
        methods: {
            fetchData(){
                console.log("method fetchData is run");
                this.error = null;
                this.post = null;
                this.loading = true;
                this.$axios.get("https://jsonplaceholder.typicode.com/posts/2")
                    .then(res=> {
                        this.loading = false;
                        console.log(res.data);
                        this.post = res.data;
                    })
                    .catch(err=> {
                        this.err = err.toString();
                    })
            }
        }
    };
    var router = new VueRouter({
        routes: [
            {
                path: '/index',
                name: 'index',
                component: Index
            },
            {
                path: '/post',
                name: 'post',
                component: Post
            }
        ]
    });

    var App = {
        template: `
            <div>
                <router-link :to="{name: 'index'}">首頁</router-link>
                <router-link :to="{name: 'post'}">個人博客</router-link>
                <router-view></router-view>
            </div>
        `
    };

    Vue.prototype.$axios = axios;
    var ap = new Vue({
        el: "#app",
        data: { },
        components: { App },
        template: '<App/>',
        router
    });
</script>
</body>
</html>

10. 導航守衛之在導航完成前獲取數據

vue-router導航守衛官方文檔
有三種方法實現導航完成前獲取數據:

  1. 經過vue-router全局守衛beforeEach;
  2. 經過watch屬性偵聽$route的變化;
  3. 經過vue-router的組件內守衛beforeUpdate
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue-router導航守衛之在導航完成前獲取數據</title>
</head>
<body>
<div id="app"></div>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="node_modules/vue-router/dist/vue-router.js"></script>
<script src="node_modules/axios/dist/axios.js"></script>
<script>
    Vue.use(vueRouter);
    var User = {
        data() {
            return {
                user: '',
                error: null,
                msg: '',  // 輸入框中輸入的內容
                msg1: '',  // 頁面中顯示的數據
                confir: true
            }
        },
        template: `
            <div>
                <input type="text" v-model="msg">
                <p>{{msg1}}</p>
                <button>保存</button>
                <div v-if="error" class="error">
                    {{error}}
                </div>
                <div class="user" v-if="user">
                    <h2>{{user}}</h2>
                </div>
            </div>
        `,
        methods: {
            setDatas(data) {
                this.user = data;
            },
            setError(err) {
                this.error = err;
            },
            saveData(){
                this.msg1 = this.msg;
                this.msg = '';
                this.confir = true
            }
        },
        beforeRouteEnter(to, from, next) {
            // 在渲染該組件的對應路由被 confirm 前調用
            // 不!能!獲取組件實例 `this`
            // 由於當守衛執行前,組件實例還沒被建立
            axios.get('http://127.0.0.1:8080/user/${to.params.id}')
                .then(res => {
                    next(vm => {
                        vm.setDatas(res.data)
                    });
                })
                .catch(err => {
                    next(vm => vm.setError(err))
                })
        },
        beforeRouteUpdate(to, from, next) {
            // 在當前路由改變,可是該組件被複用時調用
            // 舉例來講,對於一個帶有動態參數的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉的時候,
            // 因爲會渲染一樣的 Foo 組件,所以組件實例會被複用。而這個鉤子就會在這個狀況下被調用。
            // 能夠訪問組件實例 `this`
            this.user = null;
            this.$axios.get('http://127.0.0.1:8080/user/${to.params.id}')
                .then(res => {
                    this.setDatas(res.data);
                    next();
                })
                .catch(err => {
                    this.setError(err);
                    next();
                });
            next();
        },
        beforeRouteLeave(to, from, next) {
            // 導航離開該組件的對應路由時調用
            // 能夠訪問組件實例 `this`
            // 示例代碼:離開組件前檢查用戶的輸入是否保存
            if (this.msg && this.confir === true) {
                // 提示用戶保存信息
                this.confir = confirm('請保存數據');
                next(false)  // 表示不放行路由;但必須加這句代碼,不然會阻塞
            }else {
                next();
            }
        }
    };
    var Test = {
        template: '<div>這是測試組件</div>'
    };

    // 路由設置
    var router = new VueRouter({
        routes: [
            {
                path: '/user/:id',
                name: 'user'
            },
            {
                path: '/test',
                name: 'test',
                component: Test
            }

        ]
    });
    // 入口組件
    var App = {
        template: `
            <div>
                <router-link :to="{name: 'test'}">測試</router-link>
                <router-link :to="{name: 'user', params: {id: 1}}">用戶1</router-link>
                <router-link :to="{name: 'user', params: {id: 2}}">用戶2</router-link>
                <router-view></router-view>
            </div>
        `
    }

    Vue.prototype.$axios = axios;
    new Vue({
        el: "#app",
        data: {},
        components: {App},
        template: '<App/>',
        router
    })
</script>
</body>
</html>

11. 去除 URL 中的 "#"

HTML5 History 模式官網介紹
vue-router 默認使用 hash 模式,因此在路由加載的時候,項目中的 url 會自帶"#"。若是不想使用"#",可使用 vue-router 的另外一種模式:history。
mode說明:
默認值:"hash"(瀏覽器)或"abstract"(node.js)
可選值:"hash"|"history"|"abstract"

new Router({
    mode: "history",
    base: xxx,
    routes
})

當使用 history 模式時,因爲項目是單頁面應用,因此在路由跳轉的時候,可能因爲訪問不到資源而出現 404。解決辦法是在服務端增長一個覆蓋全部狀況的候選資源:若是 URL 匹配不到任何資源,則返回 index.html 頁面。

12. 路由重定向

路由重定向的詳細教程能夠閱讀 vue-router官網-重定向和別名

13. 滾動行爲

滾動行爲只有在 vue-router 的history模式下才起做用。詳細教程能夠閱讀 VueRouter 官網-滾動行爲
以下是一個簡單的示例:使用 vue-cli 建立項目,而後編輯 /src/router/index.js 文件:

import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/components/Home'
import About from '@/components/About'

Vue.use(Router);

export default new Router({
  mode: 'history',
  scrollBehavior(to, from, savedPosition) {
    // 只有調用了history.pushState()纔會觸發scrollBehavior方法。
    // return 指望滾到到哪一個的位置

    // savedPosition對象,只有在用戶點擊了前進/後退按鈕,
    // 或者是調用了go(-1)/forward()方法纔會有值,不然這個對象爲null。
    console.log(savedPosition);
    if (savedPosition){  // 判斷滾動條的位置,若是存在返回歷史位置,不然返回到起點。
      return savedPosition;
    } else {
      return {x:0, y: 0}
    }
  },
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home
    },
    {
      path: '/about',
      name: 'about',
      component: About
    }
  ]
})

編輯 /src/App.vue 文件:

<template>
  <div id="app">
    <router-link to="/">首頁</router-link>
    <router-link to="/about">關於</router-link>
    <router-view/>
  </div>
</template>
<script>
    export default {name: 'App'}
</script>
<style>
    #app {height: 2000px;}
</style>

2、axios 的使用

axios 詳細資料能夠參考axios 中文文檔

axios做爲局部模塊時,爲了使用,須要先進行掛載,掛載的方法有兩種:

  1. 使用Vue.use();的方式掛載。
  2. 使用Vue.prototype.$axios = axios;的方式掛載。

1. 基本使用

(1) 簡單 GET 請求

var App = {
    template: "<div><button @click='getData'>獲取數據</button></div>",
    methods: {
        getData() {
            this.$axios.get("http://jsonplaceholder.typicode.com/todos")  // GET請求
                .then(res => {  // 請求成功的處理邏輯
                    console.log(res.data[0]);
                })
                .catch(err => {  // 請求失敗的處理邏輯
                    console.log(err);
                })
        }
    }
};

Vue.prototype.$axios = axios;  // 將axios掛載到Vue實例
new Vue({
    el: "#app",
    template: '<App/>',
    components: { App }
})

(2) 併發請求

var App = {
    data() {
        return {
            getRes: "",
            postRes: ""
        }
    },
    template: `
        <div>
            <div>GET請求響應:{{getRes}}</div>
            <div>POST請求響應:{{postRes}}</div>
            <button @click="concurrentRequest">併發請求</button>
        </div>
    `,
    methods: {
        concurrentRequest() {
            this.$axios.defaults.baseURL = "http://jsonplaceholder.typicode.com/";   // 設置請求的baseURL
            let getReq = this.$axios.get("posts/55");   // GET請求
            let postReq = this.$axios.post("posts", "variable=helloWorld");   // POST請求

            this.$axios.all([getReq, postReq])
                .then(this.$axios.spread((res1, res2) => {
                    this.getRes = res1.data;
                    this.postRes = res2.data;
                }))
                .catch(err => {   // 任意一個請求失敗都將致使全部請求不成功
                    console.log(err);
                })
        }
    }
};

Vue.prototype.$axios = axios;  // 掛載axios到Vue實例
new Vue({
    el: "#app",
    template: '<App/>',
    components: { App }
})

2. axios 請求配置

var App = {
    template: `
        <div>
            <button @click="getData">獲取數據</button>
        </div>
    `,
    methods: {
        getData() {
            this.$axios.defaults.baseURL = "http://jsonplaceholder.typicode.com/posts/";
            this.$axios.get('', {
                params: {id: 10},  // URL參數
                transformResponse: [  // 請求返回後,執行then/catch以前,修改響應數據
                    function (data) {
                        console.log("修改以前:", data);  // 接收到的data是JSON字符串,能夠經過JSON.parse()方法解析成對象
                        data = JSON.parse(data);
                        data[0].title = "Hello World";
                        return data;
                    }
                ]
            })
                .then(res => {  // 請求返回的數據若是不通過transformResponse解析成對象,res在.then中也會被自動解析成對象
                    console.log(res.data);
                })
                .catch(err => {
                    console.log(err);
                });
            this.$axios.post('', "name=Jack", {
                transformRequest: [  // 請求發送以前執行,能夠修改請求將要提交的數據。只能用於PUT、POST、PATCH請求中
                    function (data) {
                        console.log("修改以前:", data);
                        data = "name=Rose";
                        return data;
                    }
                ]
            })
                .then(res => {
                    console.log(res.data);
                })
                .catch(err => {
                    console.log(err);
                })
        }
    }
};

Vue.prototype.$axios = axios;  // 掛載 axios
new Vue({
    el: "#app",
    template: '<App/>',
    components: {App}
})

3. axios 攔截器

在請求或響應被thencatch處理前能夠攔截它們,而後進行業務邏輯處理。
示例一:模擬登陸

var App = {
        template: "<div><button @click='sendRequest'>發送請求</button></div>",
        methods: {
            sendRequest() {
                // 添加請求攔截器
                this.$axios.interceptors.request.use(config => {
                    console.log(config);
                    // 模擬獲取cookie登陸狀態,並修改請求URL
                    let userId = localStorage.getItem("userId");
                    if (userId) { config.url = "65"; }
                    return config;
                }, function (err) {
                    return Promise.reject(err);
                });
                // 添加響應攔截器
                this.$axios.interceptors.response.use(response => {
                    console.log(response.data);
                    // 模擬登陸,返回cookie
                    if (response.data.userId === 6) {
                        localStorage.setItem('userId', response.data.userId)
                    }
                    return response;
                }, function (err) {
                    return Promise.reject(err);
                });

                this.$axios.defaults.baseURL = "http://jsonplaceholder.typicode.com/posts/";
                this.$axios.get("55")
                    .then(res => { console.log(res); })
                    .catch(err => { console.log(err); })
            }
        }
    };

    Vue.prototype.$axios = axios;
    new Vue({
        el: "#app",
        components: {App},
        template: "<App/>"
    })

示例二:數據加載動畫

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>VueRouter示例</title>
    <script src="./node_modules/vue/dist/vue.js"></script>
    <script src="./node_modules/axios/dist/axios.js"></script>
    <style>
        .loading {
            width: 80px;
            height: 40px;
            margin: 0 auto;
            margin-top: 100px;
        }

        .loading span {
            display: inline-block;
            width: 8px;
            height: 100%;
            border-radius: 4px;
            background: lightgreen;
            -webkit-animation: load 1s ease infinite;
        }

        @-webkit-keyframes load {
            0%, 100% {
                height: 40px;
                background: lightgreen;
            }
            50% {
                height: 70px;
                margin: -15px 0;
                background: lightblue;
            }
        }

        .loading span:nth-child(2) {
            -webkit-animation-delay: 0.2s;
        }

        .loading span:nth-child(3) {
            -webkit-animation-delay: 0.4s;
        }

        .loading span:nth-child(4) {
            -webkit-animation-delay: 0.6s;
        }

        .loading span:nth-child(5) {
            -webkit-animation-delay: 0.8s;
        }
    </style>
</head>
<body>
<div id="app"></div>
<script>
    let App = {
        data() {
            return {
                isShow: false,
            }
        },
        template: `
            <div>
                <div class="loading" v-show="isShow">
                    <span></span>
                    <span></span>
                    <span></span>
                    <span></span>
                    <span></span>
                </div>
                <button @click="sendAjax">數據獲取</button>
            </div>
        `,
        methods: {
            sendAjax() {
                // 添加請求攔截器
                this.$axios.interceptors.request.use((config) => {
                    this.isShow = true;
                    return config;
                }, function (error) {
                    return Promise.reject(error);
                });
                // 添加響應攔截器
                this.$axios.interceptors.response.use((response) => {
                    this.isShow = false;
                    return response;
                }, function (error) {
                    return Promise.reject(error);
                });
                // 發送請求,獲取數據
                this.$axios.get("http://jsonplaceholder.typicode.com/todos")
                    .then(res => {
                        console.log(res.data.length);
                    })
                    .catch(error => {
                        console.log(error);
                    })
            }
        }
    };

    Vue.prototype.$axios = axios;
    new Vue({
        el: "#app",
        template: '<App/>',
        components: {App}
    })
</script>
</body>
</html>

3、Vuex的使用

1. 基本介紹

Vuex的詳細使用教程能夠閱讀 Vuex 官方網站
Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態,利用 Vue.js 的數據響應機制來進行高效的狀態更新。全部的組件均可以從 Vuex 獲取狀態,以實現組件間數據的傳遞。

若是應用夠簡單,最好不要使用 Vuex,一個簡單的store模式就足夠了。可是若是須要構建的是一個大型單頁面應用,你可能會考慮如何更好得在組件外部管理狀態,Vuex 會是最好的選擇。

Vuex 的五大核心概念:State、Getter、Mutation、Action、Module。

  • State:用於存儲狀態
  • Getter:至關於計算屬性
  • Mutation:惟一修改 State 的方法就是提交 Mutation
  • Action:異步操做提交 Mutation
  • Module:將 Vuex 的狀態劃分紅不一樣的模塊

Vuex 應用的核心就是store,它實際上就是一個容器,包含着應用中大部分的狀態(State)。Vuex 和單純的全局對象有兩點不一樣:

  1. Vuex 的狀態存儲都是響應式的,當 Vue 組件從store中讀取狀態的時候,若store中的狀態發生變化,那麼組件也會相應地獲得高效更新。
  2. 不能直接改變store中的狀態,改變store中的狀態的惟一途徑就是顯式地提交 Mutation 。這樣使得咱們能夠方便地跟蹤每個狀態的變化。

修改 state 的惟一方法是提交 mutations ,可是 mutations 中的方法是同步的。Vuex 能夠經過 actions 提交 mutations 以達到異步的效果。

2. 注意事項

Vuex 的store中的狀態是響應式的,因此在 Mutation 中向 State 動態添加屬性時,也須要使用 Vue 的手動設置方法完成響應式。

在 Mutation 也須要與使用 Vue 同樣遵照一些注意事項:
(1) 最好提早在 store 中初始化好全部須要的屬性
(2) 當須要在對象上動態添加新屬性時,應該使用Vue.set(property, key, value)

使用 Vuex 時,別忘了將 Vuex 的實例化對象做爲屬性添加到 Vue 對象中,不然 Vuex 將不起做用。

3. 使用示例

4、webpack 入門

1. 安裝

  1. 執行命令npm init初始化;
  2. 執行命令npm install webpack@3.12.0 -D下載webpack

2. 簡單使用

手動實現一個簡易vue-cli腳手架工具,同時學習webpack的使用。

(1) 建立index.html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>
    <div id="app"></div>
    <script src="./dist/build.js"></script>
</body>
</html>

(2) 建立main.js做爲項目的入口文件

// ECMAScript6的模塊導入
import Vue from "vue/dist/vue"
import App from "./App.js"
import {num1, num2, add} from "./App.js"

// 導入模塊時,還能夠: import * as app from "./App.js"
// 在調用時,經過: app.num1; app.add; app.default

console.log(num1, num2);
console.log(add(3, 6));

new Vue({
    el: "#app",
    components: {App},
    template: "<App/>"
})

(3) 建立App.js組件文件

var app = {
    template: "<div>程序入口組件</div>"
};
// 三種拋出方式
export default app;  // 直接拋出
export var num1= 1;  // 聲明並拋出
var num2 = 2; export {num2};  // 先聲明,再拋出

export function add(x, y) {  // 拋出一個函數
    return x + y;
}

(4) 打包

1. 若是 npm 全局安裝 webpack,能夠執行命令`webpack ./main.js ./dist/build.js`。
2. 若是`webpack`安裝在項目目錄,能夠按以下進行配置使用:
- 設置"package.json"文件`scripts`屬性"build": "webpack ./main.js ./dist/build.js";
- 執行命令`npm run build`進行打包。

(5) build.js文件解讀

build.js 文件中有"0~6"註釋的編號,它們分別是:

  • 0: 設置一個全局變量,在 web 端指向window對象;
  • 1: 載入main.js的代碼,一個Vue實例對象;
  • 2: 載入vue源碼自己;
  • 3,4,5: 都是與node_modules/setimmediate(Vue 的 DOM 異步更新)相關;
  • 6: 與App.js解析相關。

3. webpack 打包執行順序

  1. 把全部模塊的代碼放到函數中,用一個數組保存起來;
  2. 根據require時傳入的數組索引,能知道須要哪一段代碼;
  3. 從數組中,根據索引取出包含咱們代碼的函數;
  4. 執行該函數,傳入一個對象module.exports
  5. 咱們的代碼,按照約定,正好是用module.exports = 'xxx'進行賦值;
  6. 調用函數結束後,module.exports從原來的空對象,就有值了;
  7. 最終return module.exports;做爲require函數的返回值。

4. webpack 配置文件

webpack 能夠以經過指定配置文件的方式去執行打包。

  • 當全局安裝webpack,且配置文件名稱爲預設的webpack.config.js時,能夠經過執行命令: webpack打包;
  • 當全局安裝webpack,但配置文件名稱非預設時,能夠經過執行命令: webpack --config <配置文件路徑>打包。
  • 當非全局安裝webpack,能夠將打包命令webpack --config <配置文件路徑>寫入到 package.json 文件scripts屬性中。

webpack 配置文件說明:

var path = require('path')  // node.js語句

module.exports = {
    // 入口
    entry: {
        // 能夠有多個入口,也能夠只有一個
        // 若是隻有一個,就默認從這個入口開始解析
        "main": "./main.js"
    },
    output: {
        path: path.resolve('./dist'),  // 相對路徑轉絕對路徑
        filename: "./build.js"
    },
    watch: true  // 監視文件改動,自動打包成build.js
};

5、webpack 解析器和插件

webpack 在打包過程當中遇到各類不一樣的文件時,會須要不一樣的解析器去解析相應的文件。例如:遇到.css文件時,須要用到css-loaderstyle-loader。解析器須要配置到webpack配置文件的module屬性裏。

1. CSS 文件處理

  • ES6模塊導入語法:.css文件的導入語句是import 'xxx.css'
  • 解析器下載:在命令行窗口執行命令npm i css-loader style-loader -D下載。
  • 配置webpack配置文件:
var path = require('path')

module.exports = {
    entry: {
        "main": "./main.js"
    },
    output: {
        path: path.resolve('./dist'),
        filename: "./build.js"
    },
    // 聲明模塊 包含各個loader
    module: {
        loaders: [
            {   // 添加處理css文件的loader
                test: /\.css$/,
                loader: 'style-loader!css-loader'  // 先用css-loader解析,後用style-loader載入
            }
        ]
    },
    watch: true
};

webpack 在打包過程當中,遇到.css文件,會先用css-loader解析器去解析這個文件,而後用style-loader解析器生成 style 標籤,並放到 head 標籤裏。

2. less 文件處理

  • ES6模塊導入語法:.less文件的導入語句是import 'xxx.less'
  • less 模塊下載:在命令行執行命令npm i less -D下載。
  • 解析器下載:在命令行窗口執行命令npm i less-loader -D下載。
  • 配置webpack配置文件:
var path = require('path')

module.exports = {
    entry: {
        "main": "./main.js"
    },
    output: {
        path: path.resolve('./dist'),
        filename: "./build.js"
    },
    module: {
        loaders: [
            {   // 添加處理css文件的loader
                test: /\.css$/,
                loader: 'style-loader!css-loader'
            },
            {   // 添加處理less文件的loader
                test: /\.less$/,
                loader: 'style-loader!css-loader!less-loader'
            }
        ]
    },
    watch: true
}

3. 圖片文件處理

  • ES6模塊導入語法:圖片文件的導入語句是import imgSrc from 'xxx.jpg'
  • 解析器下載:在命令行窗口執行命令npm i url-loader file-loader -D下載。
  • 配置webpack配置文件:
module.exports = {
    entry: {"main": "./main.js"},
    output: {filename: "./dist/build.js"},
    // 聲明模塊 包含各個loader
    module: {
        loaders: [
            {   // css文件處理
                test: /\.css$/,
                loader: 'style-loader!css-loader'
            },
            {   // less文件處理
                test: /\.less$/,
                loader: 'style-loader!css-loader!less-loader'
            },
            {   // 圖片文件處理
                test: /\.(jpg|png|jpeg|gif|svg)$/,
                loader: 'url-loader?limit=4000'
            }
        ]
    }
};

圖片大小比limit設置的值小時,html 頁面中會使用base64編碼載入圖片,這能夠減小圖片的網絡請求;圖片大小比limit設置的值大時,會生成一個圖片副本,html 頁面中圖片的路徑指向該副本,圖片副本會和 html 頁面混在一塊兒,致使項目的代碼結構混亂;所以設置一個合理的limit值是頗有必要的。

特別說明:
webpack 最終會將各個模塊打包成一個文件,所以樣式中的url路徑是相對於入口 html 頁面的,而不是相對於原始 CSS 文件所在路徑的,這就會致使引入失敗。這個問題是經過配置file-loader解決的,file-loader能夠解析項目中的url引入(不只限於 CSS 文件),而後根據配置將文件複製到相應的路徑,修改打包後文件的引用路徑。

4. html 文件插件

  • 插件下載:在命令行窗口執行命令npm i html-webpack-plugin --save-dev下載。
  • 配置webpack配置文件:
var path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');  // 載入插件對象

module.exports = {
    entry: {
        "main": "./src/main.js"
    },
    output: {
        path: path.resolve('./dist'),
        filename: "./build.js"
    },
    module: {
        loaders: [
            {
                test: /\.css$/,
                loader: 'style-loader!css-loader'
            },
            {
                test: /\.less$/,
                loader: 'style-loader!css-loader!less-loader'
            },
            {
                test: /\.(jpg|png|jpeg|gif|svg)$/,
                loader: 'url-loader?limit=400000'
            }
        ]
    },
    // 聲明插件
    plugins: [
        new HtmlWebpackPlugin({  // 生成html文件的插件
            template: './src/index.html'  // html源文件
        })
    ],
    watch: true
};

5. webpack-dev-server 熱加載插件

  • 插件下載:在命令行窗口執行命令npm install webpack-dev-server --save-dev下載。
  • 經常使用配置參數:
    • --open 自動打開瀏覽器
    • --hot 熱更新,不刷新替換 css 樣式
    • --inline 自動刷新
    • --port 指定端口
    • --process 顯示編譯進度
  • webpack-dev-server插件的配置,須要寫在package.json文件中:
{
  "scripts": {
    "dev": "webpack-dev-server --open --hot --inline --config ./webpack.dev.config.js"
  }
}

6. ES6 語法解析

  • 模塊下載:在命令行執行命令npm i babel-core babel-loader babel-preset-dev babel-plugin-transform-runtime -D
  • 配置webpack配置文件:
var path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');  // 載入插件對象

module.exports = {
    entry: {
        "main": "./src/main.js"
    },
    output: {
        path: path.resolve('./dist'),
        filename: "./build.js"
    },
    module: {
        loaders: [
            {    // css文件處理
                test: /\.css$/,
                loader: 'style-loader!css-loader'
            },
            {    // less文件處理
                test: /\.less$/,
                loader: 'style-loader!css-loader!less-loader'
            },
            {    // 圖片文件處理
                test: /\.(jpg|png|jpeg|gif|svg)$/,
                loader: 'url-loader?limit=400000'
            },
            {
                // 處理ES6,7,8
                test: /\.js$/,
                loader: 'babel-loader',
                exclude: '/node_modules/',  // 排除對node_modules的解析
                options: {
                    presets: ['env'], // 處理關鍵字
                    plugins: ['transform-runtime']  // 處理函數
                }
            }
        ]
    },
    // 聲明插件
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html'  // html源文件
        })
    ],
    watch: true
};

ES6 語法解析模塊介紹:
1)babel-core
babel-core 的做用是把 js 代碼分析成 ast(抽象語法樹),方便各個插件分析語法進行相應的處理。有些新語法在低版本 js 中是不存在的,如箭頭函數、rest 參數,函數默認值等,這種語言層面的不兼容只能經過將代碼轉爲 ast,分析其語法後再轉爲低版本 js。
2)babel-loader
babel-core 會使用 abel 轉譯器,abel 轉譯器提供了 babel 的轉譯API,如 babel.transform 等,用於對代碼進行轉譯。abel 轉譯器經過 babel-loader 調用這些 API 來完成將 ES6 代碼進行轉譯。因此 babel-core 和 babel-loader 須要聯合使用。
3)babel-preset-env
自行配置轉譯過程當中使用的各種插件很是麻煩,全部 babel 官方幫咱們作了一些預設的插件集,稱之爲preset。這樣咱們只須要使用對應的 preset 就能夠了。以 JS 標準爲例,babel 提供了: es201五、es201六、es201七、env。es20xx 的 preset 只轉譯該年份批准的標準;env 代指最新的標準,包括了 latest 和 es20xx 各年份。
4)babel-plugin-transform-runtime
babel 默認只轉換新的 JavaScript 語法,而不轉換新的 API。像Iterator,Generator,Set,Maps,Proxy,Reflect,Symbol,Promise等全局對象,以及一些定義在全局對象上的方法(如Object.assign)都不會轉譯。若是想使用這些新的對象和方法,必須使用 babel-polyfill 模塊,爲當前環境提供一個墊片。

7. vue組件單文件引入

  • 模塊下載:在命令行窗口執行命令npm install vue-loader@4.4.1 vue-template-compiler@2.5.17 -D下載。

vue-loader依賴於vue-template-compiler

  • 建立App.vue文件:
<template>
    <!-- 當前組件的HTML結構 -->
    <div>
        {{msg}}
    </div>
</template>

<script>
    // 當前組件的業務邏輯
    export default {
        name: "App",
        data(){
            return {
                msg: 'hello App.vue'
            }
        }
    }
</script>

<style scoped>
     /* 當前組件的樣式 */
</style>
  • 建立入口文件main.js:
import Vue from "vue"

import App from "./App.vue"

new Vue({
    el: "#app",
    render: c => c(App)
});

Render函數是Vue2.x版本新增的一個函數。它基於 JavaScript 計算,使用虛擬 DOM 來渲染節點提高性能。經過使用createElement(h)來建立 DOM 節點,createElementrender的核心方法。Vue 編譯的時候會把 template 裏面的節點解析成虛擬 DOM。

  • 配置webpack配置文件:
var path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: {
        "main": "./src/main.js"
    },
    output: {
        path: path.resolve('./dist'),
        filename: "./build.js"
    },
    module: {
        loaders: [
            {
                test: /\.css$/,
                loader: 'style-loader!css-loader'
            },
            {
                test: /\.less$/,
                loader: 'style-loader!css-loader!less-loader'
            },
            {
                test: /\.(jpg|png|jpeg|gif|svg)$/,
                loader: 'url-loader?limit=400000'
            },
            {
                // 處理vue單文件組件
                test:/\.vue$/,
                loader: 'vue-loader'
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html'
        })
    ],
    watch: true
};

8. CommonsChunkPlugin 的使用

CommonsChunkPlugin 主要是用來提取第三方庫和公共模塊,避免首屏加載的 bundle 文件或者按需加載的 bundle 文件體積過大,從而致使加載時間過長,着實是優化的一把利器。

(1) chunk(代碼塊)的分類

  • webpack當中配置的入口文件(entry)是 chunk,能夠理解爲entry chunk
  • 入口文件以及它的依賴文件經過 code splite (代碼分割)出來的也是 chunk,能夠理解爲children chunk
  • 經過 CommonsChunkPlugin 建立出來的文件也是 chunk,能夠理解爲commons chunk

(2) CommonsChunkPlugin 可配置的屬性

  • name:能夠是已經存在的 chunk (通常指入口文件)對應的name,那麼就會把公共模塊代碼合併到這個 chunk 上 ;不然,會建立名字爲namecommons chunk進行合併。
  • filename:指定commons chunk的文件名。
  • chunks:指定source chunk,即指定從那些 chunk 當中去找公共模塊,省略該選項的時候,默認就是entry chunk
  • minChunks:既能夠是數字,也能夠是函數,還能夠是 Infinity,具體用法和區別下面討論。

(3) 代碼塊分離的三種狀況

package.json中的dependences屬性記錄了項目中依賴的第三方庫。使用模塊下載命令npm install vue.js -D會將模塊添加到該屬性中。

示例背景說明:項目依賴第三方庫 Vue.js;兩個入口文件 main1.js、main2.js;入口文件都用到了自定義公共模塊 common.js。
1. 不分離出第三方庫和自定義公共模塊
修改 webpack.config.js 配置文件

const path = require('path');

module.exports = {
    entry: {  // 多入口文件的配置
        "main1": "./src/main1.js",
        "main2": "./src/main2.js"
    },
    output: {
        path: path.resolve('./dist'),
        filename: "[name].js"  // 對應多入口的多出口配置
    },
    watch: true
};

此時,第三方庫和自定義公共模塊會被打包到全部入口文件中,形成代碼冗餘及重複加載。

2. 分離出第三方庫、自定義公共模塊、webpack運行文件,但他們在同一個文件中
修改 webpack.config.js 配置文件,新增一個入口文件 vendor,並添加 CommonsChunkPlugin 插件進行模塊提取分離:

const path = require('path');
const webpack = require('webpack');  // 導入webpack運行文件
const packagejson = require('./package.json');  // 導入項目package.json文件

module.exports = {
    entry: {  // 多入口文件的配置
        "main1": "./src/main1.js",
        "main2": "./src/main2.js",
        "vendor": Object.keys(packagejson.dependencies)  // 獲取生產環境依賴的庫
    },
    output: {
        path: path.resolve('./dist'),
        filename: "[name].js"  // 對應多入口的多出口配置
    },
    watch: true,
    plugins: [
        new webpack.optimize.CommonsChunkPlugin({  // 模塊提取分離到vendor.js文件中
            name: ['vendor'],
            filename: '[name].js'
        })
    ]
};

此時第三方庫、自定義公共模塊、webpack運行文件被分離到同一個文件中。可是每次打包時,webpack 運行文件都會變,若是不分離出 webpack 運行文件,每次打包生成 vendor.js 對應的哈希值都會變化,使瀏覽器認爲緩存的 vendor.js失效,而從新去服務器中獲取。

3. 單獨分離第三方庫、自定義公共模塊、webpack運行文件,它們各自在不一樣文件中
第一步:抽離 webpack 運行文件
修改 webpack.config.js 配置文件

plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            name: ['vendor', 'runtime'],  // runtime爲抽離的webpack運行文件的名字,名字是固定的
            filename: '[name].js'
        })
    ]

上面這段代碼等價於下面這段代碼:

plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            name: 'vendor',
            filename: '[name].js'
        }),
        new webpack.optimize.CommonsChunkPlugin({  // 用於抽離webpack運行文件
            name: 'runtime',
            filename: '[name].js',
            chunks: ['vendor']  // 從哪裏抽離,即"source chunks"是誰
        })
    ]

這段抽離 webpack 運行文件的代碼的意思是:建立一個名爲 runtime 的 commons chunk 進行 webpack 運行文件的抽離,其中source chunks是 vendor.js。

第二步:抽離第三方庫和自定義公共模塊
從第三方庫中分離自定義公共模塊,必須定義minChunks屬性才能成功抽離。minChunks 能夠設置爲數字、函數和 Infinity,默認值是數字2(官方文檔說默認值爲入口文件的數量)。

minChunks取值:

  • 數字:模塊被多少個 chunk 公共引用才被抽取出來成爲commons chunk;
  • 函數:接受(module, count)兩個參數,返回一個布爾值,能夠在函數內進行規定好的邏輯來決定某個模塊是否提取成爲commons chunk
  • Infinity:只有當入口文件(entry chunks)大於 3 時才生效,用來從第三方庫中分離自定義的公共模塊。

修改 webpack.config.js 配置文件,要把第三方庫和自定義公共模塊分別單獨抽離出來,首先須要將minChunks屬性設置爲Infinity

const path = require('path');
const webpack = require('webpack');  // 導入webpack運行文件
const packagejson = require('./package.json');  // 導入項目package.json文件

module.exports = {
    entry: {  // 多入口文件的配置
        "main1": "./src/main1.js",
        "main2": "./src/main2.js",
        "vendor": Object.keys(packagejson.dependencies)  // 獲取生產環境依賴的庫
    },
    output: {
        path: path.resolve('./dist'),
        filename: "[name].js"  // 對應多入口的多出口配置
    },
    watch: true,
    plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            name: ['vendor', 'runtime'],
            filename: '[name].js',
            minChunks: Infinity  // 設置minChunks屬性
        }),
        new webpack.optimize.CommonsChunkPlugin({
            name: 'common',
            filename: '[name].js',
            chunks: ['main1.js', 'main2.js']  // 從哪些文件中抽取commons chunk
        })
    ]
};

此時 vendor.js、第三方文件、自定義公共模塊、webpack 運行文件就抽離出來,並分別在不一樣文件中。

9. webpack.ensure 異步加載

webpack.ensure有人稱爲異步加載,也有人叫它代碼切割。其實就是把 JS 模塊獨立導出到一個.js文件,而後在使用這個模塊的時候,webpack 會構造script dom元素,由瀏覽器發起異步請求獲取這個.js文件。

webpack.ensure 的原理:
把一些 JS 模塊獨立成一個個.js文件,而後須要用到的時候,再建立一個script對象,加入到document.head對象中。瀏覽器會自動發起請求,去請求這個.js文件,再經過回調函數,去定義獲得這個.js文件後,須要執行什麼業務邏輯操做。

示例背景說明
main.js依賴三個js文件:
(1) A.js是封裝aBtn按鈕點擊後才執行的業務邏輯;
(2) B.js是封裝bBtn按鈕點擊後才執行的業務邏輯;
(3) vue.js是封裝了main.js須要利用的包。
A.js和B.js都不是main.js必須的,都是將來纔可能發生的操做,那麼能夠利用異步加載,當發生的時候再去加載。
vue.js是main.js當即依賴的工具箱,但它又很是大,因此將其配置打包成一個公共模塊,利用瀏覽器的併發加載,加快下載速度。

index.html 文件:

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>webpack的使用</title>
</head>
<body>
    <div id="app"></div>
    <button id="aBtn">A-btn</button>
    <br>
    <button id="bBtn">B-btn</button>
</body>
</html>

main.js 文件

// ECMAScript6的模塊導入
import Vue from "vue"
console.log(Vue);

document.getElementById('aBtn').onclick = function () {
    // 異步的加載A.js
    require.ensure([], function () {
        var A = require("./A.js");
        alert(A.data);
    })

};

document.getElementById('bBtn').onclick = function () {
    // 異步的加載B.js
    require.ensure([], function () {  // ensure函數的第一個參數(數組[])用於添加回調函數中異步加載的JS文件的依賴文件的路徑
        var B = require("./B.js");
        alert(B.data);
    })
};

A.js & B.js

// A.js
var A = {
    "data": "Hello A"
};
module.exports = A;

// B.js
var B = {
    "data": "Hello B"
};
module.exports = B;

webpack 配置文件

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');  // 導入webpack運行文件
const packagejson = require('./package.json');  // 導入項目package.json文件

module.exports = {
    entry: {
        "main": "./src/main.js",
        "util": Object.keys(packagejson.dependencies)
    },
    output: {
        path: path.resolve('./dist'),
        filename: "[name].js"  // 對應多入口的多出口配置
    },
    watch: true,
    plugins: [
        new webpack.optimize.CommonChunkPlugin({
            name: "common",
            filename: "[name].js"
        }),
        new HtmlWebpackPlugin({
            // 主要用於多入口文件,當有多個入口文件的時候,它就會編譯生成多個打包後的文件,chunks就能選擇你要使用哪些JS文件
            chunks: ["common", "util", "main"],
            template: "./src/index.html",
            inject: true  // inject有四個值 true、body、head
        })
    ]
};

6、RESTful 規範

RESTful規範是一種軟件的架構風格、設計風格,而不是標準,爲客戶端和服務端的交互提供一組設計原則和約束條件。
先後端分離:

  • 後端提供接口(API)
  • 前端寫頁面和Ajax技術

1. 面向資源編程

每一個URL表明一種資源,URL中儘可能不要使用動詞,要用名詞,每每名詞跟數據庫表格相對應。通常來講,數據庫中的表都是同種記錄的集合,全部API中的名詞也應該使用複數。
例如:一個提供動物園信息的API,包括各類動物和僱員的信息,它的路徑應該設計成:

https://api.example.com/v1/zoos
https://api.example.com/v1/animals
https://api.example.com/v1/employees

RESTful規範參考

2. 在URL中的過濾條件

若是記錄數量不少,服務器不可能將全部的數據都返回給用戶。API應該提供參數,用於過濾返回結果。

?limit=10: 指定返回記錄的數量
?offset=10: 指定返回記錄的開始位置
?page=2&per_page=100: 指定第幾頁,以及每頁的記錄數
?sortby=name&order=asc: 指定返回結果按照哪一個屬性排序,以及排序順序
?item_id=1: 指定篩選條件

3. 儘可能使用HTTPS

4. 相應時設置狀態碼

5. 返回錯誤信息

若是狀態碼是4XX,應該向用戶返回錯誤信息。通常來講,返回的信息中將error做爲鍵名,錯誤信息做爲鍵值便可。

6. Hypermedia API

若是遇到須要跳轉的狀況,那麼就要攜帶跳轉接口的URL。
Hypermedia API 的設計,好比github的API就是這種設計。訪問api.github.com就會獲得一個全部可用的API的網址列表。

7. 其餘

  1. API的身份認證應該使用OAuth 2.0框架
  2. 服務器返回的數據格式,應該儘可能使用JSON,避免使用XML

7、其餘前端功能庫補充

1. qs 庫

qs 庫(模塊)是一個增長了一些安全性的查詢字符串解析和序列化字符串的庫。

相關文章
相關標籤/搜索