vue-router官方文檔javascript
路由實現方式:css
<!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>
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/>" })
<a>
標籤,它的to
屬性至關於<a>
標籤的href
屬性;命名路由就是給路由規則添加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/>" })
路由參數包括: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() } }
經過路由組件傳參,能夠實現同一組件根據參數的不一樣顯示不一樣的內容,達到組件的複用。詳見vue-router官方網站
當子路由是不一樣的頁面結構時,能夠經過嵌套路由來根據路由加載不一樣的組件。定義子路由:在路由對象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/>" })
內置組件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/>" })
示例代碼知識點總結:
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> ` })
需求:在導航完成以後加載數據,渲染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>
vue-router導航守衛官方文檔
有三種方法實現導航完成前獲取數據:
beforeEach
;watch
屬性偵聽$route
的變化;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>
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 頁面。
路由重定向的詳細教程能夠閱讀 vue-router官網-重定向和別名。
滾動行爲只有在 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>
axios 詳細資料能夠參考axios 中文文檔
axios
做爲局部模塊時,爲了使用,須要先進行掛載,掛載的方法有兩種:
Vue.use();
的方式掛載。Vue.prototype.$axios = axios;
的方式掛載。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 } })
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 } })
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} })
在請求或響應被then
或catch
處理前能夠攔截它們,而後進行業務邏輯處理。
示例一:模擬登陸
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>
Vuex的詳細使用教程能夠閱讀 Vuex 官方網站。
Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態,利用 Vue.js 的數據響應機制來進行高效的狀態更新。全部的組件均可以從 Vuex 獲取狀態,以實現組件間數據的傳遞。
若是應用夠簡單,最好不要使用 Vuex,一個簡單的store模式就足夠了。可是若是須要構建的是一個大型單頁面應用,你可能會考慮如何更好得在組件外部管理狀態,Vuex 會是最好的選擇。
Vuex 的五大核心概念:State、Getter、Mutation、Action、Module。
Vuex 應用的核心就是store
,它實際上就是一個容器,包含着應用中大部分的狀態(State)。Vuex 和單純的全局對象有兩點不一樣:
store
中讀取狀態的時候,若store
中的狀態發生變化,那麼組件也會相應地獲得高效更新。store
中的狀態,改變store
中的狀態的惟一途徑就是顯式地提交 Mutation 。這樣使得咱們能夠方便地跟蹤每個狀態的變化。修改 state 的惟一方法是提交 mutations ,可是 mutations 中的方法是同步的。Vuex 能夠經過 actions 提交 mutations 以達到異步的效果。
Vuex 的store
中的狀態是響應式的,因此在 Mutation 中向 State 動態添加屬性時,也須要使用 Vue 的手動設置方法完成響應式。
在 Mutation 也須要與使用 Vue 同樣遵照一些注意事項:
(1) 最好提早在 store 中初始化好全部須要的屬性
(2) 當須要在對象上動態添加新屬性時,應該使用Vue.set(property, key, value)
使用 Vuex 時,別忘了將 Vuex 的實例化對象做爲屬性添加到 Vue 對象中,不然 Vuex 將不起做用。
npm init
初始化;npm install webpack@3.12.0 -D
下載webpack
。手動實現一個簡易
vue-cli
腳手架工具,同時學習webpack
的使用。
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>
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/>" })
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; }
1. 若是 npm 全局安裝 webpack,能夠執行命令`webpack ./main.js ./dist/build.js`。 2. 若是`webpack`安裝在項目目錄,能夠按以下進行配置使用: - 設置"package.json"文件`scripts`屬性"build": "webpack ./main.js ./dist/build.js"; - 執行命令`npm run build`進行打包。
build.js
文件解讀build.js 文件中有"0~6"註釋的編號,它們分別是:
window
對象;main.js
的代碼,一個Vue
實例對象;vue
源碼自己;node_modules/setimmediate
(Vue 的 DOM 異步更新)相關;App.js
解析相關。require
時傳入的數組索引,能知道須要哪一段代碼;module.exports
;module.exports = 'xxx'
進行賦值;module.exports
從原來的空對象,就有值了;return module.exports;
做爲require
函數的返回值。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 };
webpack 在打包過程當中遇到各類不一樣的文件時,會須要不一樣的解析器去解析相應的文件。例如:遇到.css
文件時,須要用到css-loader
和style-loader
。解析器須要配置到webpack
配置文件的module
屬性裏。
.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 標籤裏。
.less
文件的導入語句是import 'xxx.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 }
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 文件),而後根據配置將文件複製到相應的路徑,修改打包後文件的引用路徑。
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 };
npm install webpack-dev-server --save-dev
下載。webpack-dev-server
插件的配置,須要寫在package.json
文件中:{ "scripts": { "dev": "webpack-dev-server --open --hot --inline --config ./webpack.dev.config.js" } }
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 模塊,爲當前環境提供一個墊片。
npm install vue-loader@4.4.1 vue-template-compiler@2.5.17 -D
下載。
vue-loader
依賴於vue-template-compiler
。
<template> <!-- 當前組件的HTML結構 --> <div> {{msg}} </div> </template> <script> // 當前組件的業務邏輯 export default { name: "App", data(){ return { msg: 'hello App.vue' } } } </script> <style scoped> /* 當前組件的樣式 */ </style>
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 節點,createElement
是render
的核心方法。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 };
CommonsChunkPlugin 主要是用來提取第三方庫和公共模塊,避免首屏加載的 bundle 文件或者按需加載的 bundle 文件體積過大,從而致使加載時間過長,着實是優化的一把利器。
entry chunk
;children chunk
;commons chunk
name
,那麼就會把公共模塊代碼合併到這個 chunk 上 ;不然,會建立名字爲name
的commons chunk
進行合併。commons chunk
的文件名。source chunk
,即指定從那些 chunk 當中去找公共模塊,省略該選項的時候,默認就是entry chunk
。
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取值:
commons chunk
;commons chunk
;修改 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 運行文件就抽離出來,並分別在不一樣文件中。
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 }) ] };
RESTful規範是一種軟件的架構風格、設計風格,而不是標準,爲客戶端和服務端的交互提供一組設計原則和約束條件。
先後端分離:
每一個URL表明一種資源,URL中儘可能不要使用動詞,要用名詞,每每名詞跟數據庫表格相對應。通常來講,數據庫中的表都是同種記錄的集合,全部API中的名詞也應該使用複數。
例如:一個提供動物園信息的API,包括各類動物和僱員的信息,它的路徑應該設計成:
https://api.example.com/v1/zoos https://api.example.com/v1/animals https://api.example.com/v1/employees
若是記錄數量不少,服務器不可能將全部的數據都返回給用戶。API應該提供參數,用於過濾返回結果。
?limit=10: 指定返回記錄的數量 ?offset=10: 指定返回記錄的開始位置 ?page=2&per_page=100: 指定第幾頁,以及每頁的記錄數 ?sortby=name&order=asc: 指定返回結果按照哪一個屬性排序,以及排序順序 ?item_id=1: 指定篩選條件
若是狀態碼是4XX,應該向用戶返回錯誤信息。通常來講,返回的信息中將error
做爲鍵名,錯誤信息做爲鍵值便可。
若是遇到須要跳轉的狀況,那麼就要攜帶跳轉接口的URL。
Hypermedia API 的設計,好比github的API就是這種設計。訪問api.github.com就會獲得一個全部可用的API的網址列表。
qs 庫(模塊)是一個增長了一些安全性的查詢字符串解析和序列化字符串的庫。