在这一期的学习进度中,咱们会开始学习在咱们工做开发中真正要使用的开发模式,vue-cli3脚手架的运用以及Vue-router和状态管理仓Vuexjavascript
安装node环境css
由于Vue-cli3也是基于webpack搭建的,因此咱们仍是必需要用到node,若是你的电脑尚未安装node的话,能够到node官网自行下载安装,nodejs.org/zh-cn/ 推荐你们下载10.14.0版本。 安装事后 到命令行执行 node -v 检查版本,若是弹出版本 v10.14.0的话 恭喜你 安装成功,咱们开始进行下面的步骤了。html
全局安装vue-cli3 Vue CLI 的包名称由 vue-cli 改为了 @vue/cli。 若是你已经全局安装了旧版本的 vue-cli (1.x 或 2.x),你须要先经过 npm uninstall vue-cli -g 卸载它。vue
官方要求安装的node版本是大于8.9的,咱们安装的node为10.14.0,若是你自己的版本低于8.9 那么能够用nvm进行升级java
而后咱们到命令行执行node
npm install -g @vue/cli
复制代码
等待运行完毕,咱们命令行执行react
vue --version
复制代码
若是版本在3.0以上,恭喜你安装成功了webpack
接下来咱们在命令行运行git
vue create hello-world
复制代码
这时候它会提醒咱们来选择须要安装的选项es6
Vue CLI v3.1.3
┌───────────────────────────┐
│ Update available: 3.2.1 │
└───────────────────────────┘
? Please pick a preset: (Use arrow keys)
> my (vue-router, vuex, less, babel, eslint)
default (babel, eslint)
Manually select features
复制代码
第一个是我已经保存过的了,咱们初次搭建,选择 Manually select features
Vue CLI v3.1.3
┌───────────────────────────┐
│ Update available: 3.2.1 │
└───────────────────────────┘
? Please pick a preset: Manually select features
? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to invert selection)
>( ) Babel
( ) TypeScript
( ) Progressive Web App (PWA) Support
( ) Router
( ) Vuex
( ) CSS Pre-processors
( ) Linter / Formatter
( ) Unit Testing
( ) E2E Testing
复制代码
咱们看到如上界面之后,选择 Babel Router Vuex CSS Pre-processors (使用空格键选中) 进行下一步
Vue CLI v3.1.3
┌───────────────────────────┐
│ Update available: 3.2.1 │
└───────────────────────────┘
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Router, Vuex, CSS Pre-processors
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS
? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In package.json
? Save this as a preset for future projects? Yes
? Save preset as: MyTest
复制代码
接下来你们按照如上配置,基本能够完成项目搭建。等待安装完毕后执行
cd helloworld
npm run serve
复制代码
进入浏览器 打开 localhost:8080 若是看到Vue的欢迎界面,恭喜你,咱们已经迈出成功第一步了
在Vue-cli3 中 为你们提供了图形化配置界面 执行
vue ui
复制代码
会开到提示在 localhost:8000打开了UI配置界面,因为我的感受图形化配置仍是不灵活的,这里就先不给你们作太多演示了,要想作好一个VUE项目,须要作太多的搭配,我这里会配置一套基本可使用的VUE框架放到我的github供你们使用。
<template>
<!-- 这里面写html -->
</template>
<script> // 若是要引入外部的文件 请在 export default外面引入 // 例如 // @ is an alias to /src import HelloWorld from '@/components/HelloWorld.vue' //这里引入了 HelloWorld组件 export default { name: 'home', components: { HelloWorld //使用组件 } } export default { name:'vue', //这里面就写VUE组件内的属性便可 } </script>
<style> /* 这里写css 固然你也能够写 lass scss 须要babel支持而且在 style上添加 lang属性 例如 <style lang="scss"> */ </style>
复制代码
咱们使用VUE通常是用来开发单页程序,那么在单页程序内的跳转就要用到路由的形式,因为这里课程是要带你们快速熟悉VUE而且使用VUE,因此原理这里暂且跳过,直接告诉你们使用方法。 咱们在开始选择框架要用到的东西的时候已经选择了Vue-Router,这里咱们直接上代码使用便可。咱们这里就搭配着Vue-Cli3的脚手架来说解,这里顺便为你们讲解一下Vue-cli3脚手架为咱们建立的目录结构
│ .gitignore git屏蔽提交文件 │ babel.config.js 自定义babel的地方 │ package.json 你的全部依赖啥的 │ README.md 不介绍 │ yarn.lock yarn 若是你是 npm 就是 package-lock.json │
├─public 经过执行 npm run build 产生 │ favicon.ico │ index.html │
└─src 主要咱们要修改的地方 │ App.vue App主组件 │ main.js 入口JS │ router.js 路由文件 │ store.js vueX store 文件 │
├─assets 静态文件存放处 │ logo.png │
├─components 咱们本身写的组件 │ HelloWorld.vue │
└─views VUE页面,这么安排为了方便区分是组件仍是用于路由跳转的页面 About.vue Home.vue
咱们直接看到入口文件
// main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router' //在入口文件引用了router 而且 在newVue的时候 把router挂在到了router属性上
import store from './store'
Vue.config.productionTip = false
new Vue({
router, // 挂载
store,
render: h => h(App)
}).$mount('#app')
复制代码
接着咱们看到 router.js文件
//router.js
import Vue from 'vue'
import Router from 'vue-router' //这里引用了vue-router 而且用Vue.use来使用Router
import Home from './views/Home.vue'
Vue.use(Router)
// Router的内置属性
export default new Router({
mode: 'history', //要使用hash模式仍是 history模式 咱们通常状况下仍是用 hash模式 history在后台支持的状况下能够开启
base: process.env.BASE_URL, // 应用的基路径。例如,若是整个单页应用服务在 /app/ 下,而后 base 就应该设为 "/app/"
routes: [ //这就是真正写路由的地方了
{
path: '/', // 当路由是/的时候 咱们匹配哪一个组件(这里是Home.vue)
name: 'home',
component: Home
},
{
path: '/about', // 当路由是/about的时候 咱们匹配哪一个组件(About.vue)
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ './views/About.vue') //路游懒加载,推荐使用这种模式,不过咱们须要改写一下
}
]
})
复制代码
稍微作一下懒加载模式的改写
//router.js
import Vue from 'vue'
import Router from 'vue-router' //这里引用了vue-router 而且用Vue.use来使用Router
Vue.use(Router)
const Home = ()=> import('./views/Home.vue') //使用常量模式 提早声明 而且在component中使用 这样咱们就完成了一个能够简单实用的路由了
const About = ()=> import('./views/About.vue')
// Router的内置属性
export default new Router({
mode: 'history', //要使用hash模式仍是 history模式 咱们通常状况下仍是用 hash模式 history在后台支持的状况下能够开启
base: process.env.BASE_URL, // 应用的基路径。例如,若是整个单页应用服务在 /app/ 下,而后 base 就应该设为 "/app/"
routes: [ //这就是真正写路由的地方了
{
path: '/', // 当路由是/的时候 咱们匹配哪一个组件(这里是Home.vue)
name: 'home',
component: Home
},
{
path: '/about', // 当路由是/about的时候 咱们匹配哪一个组件(About.vue)
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: About
}
]
})
复制代码
那么 这些路由组件怎么展现呢?在哪里展现呢? 这里咱们要看到 App.vue文件
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link>
<!-- 使用router-link标签 添加 to属性决定咱们要路由到什么地方 -->
<router-link to="/about">About</router-link>
</div>
<router-view/>
<!-- router-view 就是咱们要展现路由的地方 -->
</div>
</template>
<style> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; } #nav { padding: 30px; } #nav a { font-weight: bold; color: #2c3e50; } #nav a.router-link-exact-active { color: #42b983; } </style>
复制代码
到这里 咱们一个简单的基础的路由已经写好了 可使用了
如何建立子路由呢?其实也是至关简单的
//router.js
import Vue from 'vue'
import Router from 'vue-router' //这里引用了vue-router 而且用Vue.use来使用Router
Vue.use(Router)
const Home = ()=> import('./views/Home.vue') //使用常量模式 提早声明 而且在component中使用 这样咱们就完成了一个能够简单实用的路由了
const About = ()=> import('./views/About.vue')
const Children = ()=> import('./views/Children.vue') //咱们引入这个页面 页面内容
// Router的内置属性
export default new Router({
// mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home,
children:[
{
path: 'about',
name: 'about',
component: About,
},
{
path:'children',
name:'children',
component:Children
}, //这时候咱们访问 访问about 其实是home的子路由了
// 它是home的子路由,因此我們须要在home里面再书写一个路由显示的组件 <router-view/> 子路由的内容就会显示在其 router-view下面
]
}
]
})
复制代码
这里我把改进后的 app.vue 和 about.vue 等代码贴出。
<!-- app -->
<template>
<div id="app">
<router-view/>
</div>
</template>
<style> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; } #nav { padding: 30px; } #nav a { font-weight: bold; color: #2c3e50; } #nav a.router-link-exact-active { color: #42b983; } </style>
复制代码
<!-- about -->
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>
复制代码
<template>
<!-- children -->
<div class="children">
<h1>This is an children page</h1>
</div>
</template>
复制代码
<!-- home -->
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<div>
<!-- <router-link to="/">Home</router-link> | -->
<router-link to="/about">About</router-link> |
<router-link to="/children">children</router-link>
</div>
<router-view></router-view>
</div>
</template>
<script> // @ is an alias to /src import HelloWorld from '@/components/HelloWorld.vue' export default { name: 'home', components: { HelloWorld } } </script>
复制代码
编程式的导航 router.push 编程式的导航传参咱们须要使用 this.$router.push() 这个方法 咱们有三种传参方式
<script> // 字符串 // 这种方式能够实现路由跳转 可是没有办法获取到参数 没有办法传参 this.$router.push("children"); // 对象传参 // 这里面咱们就要用到路由里面的name属性了 // 命名路由传递参数须要使用params来传递,目标页面接收传递参数时使用this.$route.params // 特别注意:命名路由这种方式传递的参数,若是在目标页面刷新是会出错的 this.$router.push({ name: 'children', params: { userId: 'qm' }}) // 想要传递参数主要就是以对象的方式来写,分为两种方式:命名路由、查询参数,下面分别说明两种方式的用法和注意事项。 // 查询参数 this.$router.push({ path: '/children', query: { userId: 'qm' }}); // 这种方式参数是暴露在地址栏上面的 //查询参数其实就是在路由地址后面带上参数和传统的url参数一致的,传递参数使用query并且必须配合path来传递参数而不能用name,目标页面接收传递的参数使用query。 //注意:和name配对的是params,和path配对的是query </script>
复制代码
声明式的导航 声明式的导航也分为三种方式
<!-- 字符串 -->
<router-link to="children">click to news page</router-link>
<!-- 命名路由 -->
<router-link :to="{ name: 'children', params: { userId: 'qm'}}">click to news page</router-link>
<!-- 查询参数 -->
<router-link :to="{ path: '/children', query: { userId: 'qm'}}">click to news page</router-link>
复制代码
1.命名路由搭配params,刷新页面参数会丢失 2.查询参数搭配query,刷新页面数据不会丢失 3.接受参数使用this.$router后面就是搭配路由的名称就能获取到参数的值
url 传参
这种传参方法咱们须要在书写路由的时候作一个小小的改动
//router.js
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
const Home = ()=> import('./views/Home.vue')
const About = ()=> import('./views/About.vue')
const Children = ()=> import('./views/Children.vue')
// Router的内置属性
export default new Router({
// mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home,
children:[
{
path: 'about/:id', // 咱们在后面加入/:id 这样咱们在url输入的地址相应就变成了 /about/3 咱们在about组件内 经过 this.$route.params 如何动态监听路由的变化呢?要知道当咱们仅仅参数变化组件但是没有刷新的,提示一下 watch但是能够监听某些数据哦~实战课程我会带你们了解一下这个该如何运用
name: 'about',
component: About,
},
{
path:'children',
name:'children',
component:Children
},
]
}
]
})
复制代码
相应页面改动以下 home.vue about.vue
<!-- about -->
<template>
<div class="about">
<h1>This is an about page</h1>
{{this.$route.params.id}}
</div>
</template>
复制代码
<!-- home -->
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<div>
<router-link to="/about/3">About</router-link> |
<router-link to="/children">children</router-link>
</div>
<router-view></router-view>
</div>
</template>
<script> // @ is an alias to /src import HelloWorld from '@/components/HelloWorld.vue' export default { name: 'home', components: { HelloWorld }, } </script>
复制代码
404的制做
其实vue-router里面还有不少细节内容,这里因为咱们是基础课程因此不作太详细的介绍,相信你们在浏览网页的时候常常会看到404页面,那么用咱们的vue-router如何去实现一个404呢?其实是很是简单的 咱们须要在router.js里面书写一个通配路径放置在最后位置,当全部的路径都不匹配的时候,就会去通配这样一个404页面提示你们页面丢失了,下面我详细给你们书写一下
// router.js
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
const Home = ()=> import('./views/Home.vue')
const About = ()=> import('./views/About.vue')
const Children = ()=> import('./views/Children.vue')
const NotFound = ()=> import('./views/notFound.vue')
// Router的内置属性
export default new Router({
// mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home,
children:[
{
path: 'about/:id',
name: 'about',
component: About,
},
{
path:'children',
name:'children',
component:Children
},
]
},
{
path: '*',
name: '404',
component: NotFound,
}
]
})
复制代码
学完这些,你对于vue-router的基本运用已经能够算是合格了,工做中的大部分用法也都接触到了,咱们接下来说解vuex。
首先来一个小demo展现一下vuex的具体用途
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
number:0,
},
mutations: {
ADD(state){
state.number++
},
SUB(state){
state.number--
}
},
actions: {
}
})
复制代码
<!-- children.vue -->
<template>
<div class="children">
<h1>This is an children page</h1>
<button @click="add">
+
</button>
{{$store.state.number}}
<button @click="sub">
-
</button>
</div>
</template>
<script> export default{ name: 'children', methods: { add() { this.$store.commit('ADD') }, sub() { this.$store.commit('SUB') } } } </script>
复制代码
这样一个小小的demo实际上已经诠释了咱们vuex的做用,咱们须要在刷新以前永久保留的状态,而且想要远距离传参而且及时作出响应,那么均可以使用vuex来进行。它就是一个状态管理和加工的仓库,一共有五个重要属性,state,mutations,actions , getter , module 这么五个小玩意 我会带你们一个一个认识他们的做用,而且教会你们基本用法,固然了,它也是有较为高级的小用法的。咱们实战课也会使用稍微高级的用法来说解。
咱们用VUE文件来类比讲解store state就至关于vue中的data属性,全部的状态或者说是数据都存储在这个state里面,咱们在别的地方获取须要使用 this.$store.state.属性名称 来获取相应的值,而且咱们能够经过 mutations 和 actions 来改变state的值,从而触发全部使用到state的地方刷新。state里能够存储各类数据类型,data里面能够用的数据类型,state里面一样可使用。
getters咱们类比到vue中,那么它应该是 computed了 咱们在使用的时候 要使用 this.$store.getters.属性名 用法也和computed相似,它其实是调用一个方法,而后获取到的数据是通过一系列处理后而且return回来的数据,它的具体写法是。
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
number:0,
},
mutations: {
ADD(state){
state.number++
},
SUB(state){
state.number--
}
},
actions: {
},
getters:{
getNumber(state){ //getter的书写方法
return state.number + 100
}
}
})
复制代码
<!-- children.vue -->
<template>
<div class="children">
<h1>This is an children page</h1>
<button @click="add">
+
</button>
{{$store.state.number}}
{{$store.getters.getNumber}}
<!-- getters的基础调用方法,固然还有更高级的 实战课会讲解 -->
<button @click="sub">
-
</button>
</div>
</template>
<script> export default{ name: 'children', methods: { add() { this.$store.commit('ADD') }, sub() { this.$store.commit('SUB') } } } </script>
复制代码
这就是getters的简单实用
mutations类比到vuex中应该是 methods 它是更改 Vuex 的 store 中的状态的惟一方法是提交 mutation。Vuex 中的 mutation 很是相似于事件:每一个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是咱们实际进行状态更改的地方,而且它会接受 state 做为第一个参数:
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
number:0,
},
mutations: {
ADD(state){ //咱们把state传入 就能够对 state内的数据进行咱们想要的变化,它必须按照咱们想要获得的格式去变化, 想要直接提交mutations 须要使用 this.$stote.commit('mutations的方法名',参数) 若是想要在mutations方法中传参,写法就要变成 ADD(state,形参){ ... } 这样一种形式了 我举个例子
state.number++
},
SUB(state){
state.number--
},
// ADDPARAM(state,param){
// if (typeof param !== 'number'){
// param = 0
// }
// state.number = state.number + param
// } 这个方法就能够传入咱们想用的参数了,相应调用的地方也须要改变
},
actions: {
},
getters:{
getNumber(state){ //getter的书写方法
return state.number + 100
}
}
})
复制代码
带参数的commit如何使用
<!-- children.vue -->
<template>
<div class="children">
<h1>This is an children page</h1>
<button @click="add">
+
</button>
{{$store.state.number}}
{{$store.getters.getNumber}}
<!-- getters的基础调用方法,固然还有更高级的 实战课会讲解 -->
<button @click="sub">
-
</button>
</div>
</template>
<script> export default{ name: 'children', methods: { add() { this.$store.commit('ADD') }, // addparam() { // this.$store.commit('ADDPARAM',5) // }, // this.$store.commit('要提交的mutations名字',要传入的参数) sub() { this.$store.commit('SUB') } } } </script>
复制代码
actions属性用法和mutations相似,可是actions咱们是不能够修改state的 须要在actions经过commit来调用mutations来修改数据,那么action的意义何在呢?处理异步事件就要用action来作了呀。调用方法是,this.$store.dispatch("action的名字",参数)
书写的方法呢 我给你们展现一下
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
number:0,
},
mutations: {
ADD(state){
state.number++
},
SUB(state){
state.number--
},
ADDPARAM(state,param){
if (typeof param !== 'number'){
param = 0
}
state.number = state.number + param
}
},
actions: {
ASYNCADD(context,param){ //这里咱们传入context上下文,里面包含 commit, state ,getters 这三个属性均可以经过context来调用到而且触发内部方法
setTimeout(function(){
context.commit('ADDPARAM',param)
},1000)
}
},
getters:{
getNumber(state){ //getter的书写方法
return state.number + 100
}
}
})
复制代码
action的使用
<!-- children.vue -->
<template>
<div class="children">
<h1>This is an children page</h1>
<button @click="add">
+
</button>
{{$store.state.number}}
{{$store.getters.getNumber}}
<button @click="sub">
-
</button>
<button @click="actAdd">action</button>
</div>
</template>
<script> export default{ name: 'children', methods: { add() { this.$store.commit('ADD') }, actAdd(){ // 咱们经过 dispatch来调用action 而后由action去提交咱们的 mutations来达到异步更改状态的目的 this.$store.dispatch('ASYNCADD',50) }, sub() { this.$store.commit('SUB') } } } </script>
复制代码
vuex是否是很是简单呢,到这里你们可能在想,若是个人项目很大 我有不少的这些个方法啊,状态啊,我都写在这里不就乱了吗?没错,确定乱了,因此vuex还提供了modules 方便咱们分块管理
因为使用单一状态树,应用的全部状态会集中到一个比较大的对象。当应用变得很是复杂时,store 对象就有可能变得至关臃肿。 为了解决以上问题,Vuex 容许咱们将 store 分割成模块(module)。每一个模块拥有本身的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行一样方式的分割
咱们把上面的这些个代码分割出去
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const children = { // 由于咱们是写在了children这个组件里面 这个命名规则也好让咱们知道它存在于哪里
state: {
number:0,
},
mutations: {
ADD(state){
state.number++
},
SUB(state){
state.number--
},
ADDPARAM(state,param){
if (typeof param !== 'number'){
param = 0
}
state.number = state.number + param
}
},
actions: {
ASYNCADD(context,param){
setTimeout(function(){
context.commit('ADDPARAM',param)
},1000)
}
},
getters:{
getNumber(state){
return state.number + 100
}
}
}
export default new Vuex.Store({
modules:{
children, // 这里咱们把children传入之后 仍是同样可使用咱们的哪些方法,不过咱们是state要加上 modules名字 因此咱们的children.vue 要相应修改
}
})
复制代码
<!-- children.vue -->
<template>
<div class="children">
<h1>This is an children page</h1>
<button @click="add">
+
</button>
{{$store.state.children.number}}
<!-- 你们能够看到,这里须要加上modules名字才能够获取到number 可是其他的不须要处理,因此咱们开发中通常想要获取到 state 最好是经过getter来获取 -->
{{$store.getters.getNumber}}
<button @click="sub">
-
</button>
<button @click="actAdd">action</button>
</div>
</template>
<script> export default{ name: 'children', methods: { add() { this.$store.commit('ADD') }, actAdd(){ // 咱们经过 dispatch来调用action 而后由action去提交咱们的 mutations来达到异步更改状态的目的 this.$store.dispatch('ASYNCADD',50) }, sub() { this.$store.commit('SUB') } }, } </script>
复制代码
不少时候,咱们在正式使用中都会加上命名空间,也就是 modules里面的 namespaced 属性咱们让它变为 namespaced: true, 若是你们想要学习,能够去vuex文档学习,得益于es6 vuex给咱们提供了 mapState, mapGetters, mapActions 和 mapMutations 这四巨头 咱们能够轻松使用命名空间 这里不作过多讲解,实战课程中咱们边用边说。