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 库(模块)是一个增长了一些安全性的查询字符串解析和序列化字符串的库。