今天我们将学习如何用VUE构建一个简单的单页应用(SPA)
如果没有其他特殊声明,此教程中的VUE全部指的是VUE2.X版本
预览
让我们先看看最终的的单页程序是什么样子的
成果
阅读本教程之前希望你能有如下的基础知识:
VUE基础
如何创建VUE组件
如果你没有任何VUE或者VUE组件的知识,可以看我之前的文章
VueJS简明教程(一)之基本使用方法
使用Vue CLI 脚手架
我们将使用VUE提供的脚手架模块Vue CLI,它可以使我们构建的程序兼容ES5版本的浏览器。
NOTE: 当然这需要你在Node.js环境下进行开发,如果你还没有Node.js和NPM的基本知识,建议你花半个小时的时间配置好Node.js环境,相信我,很简单,百度随便一搜就出来一大把教程。
如何你还没有安装Vue CLI,你可以用下面的命令进行安装
npm install -g vue-cli
NOTE: vue-cli已经有了3.0版本,改名为 @vue/cli, 但是当前vue-cli还是可以使用的,因为大部分用户还是在用vue-cli,所以本教程也继续使用vue-cli作为教学。
安装完Vue CLI,我们将通过下面的命令构建我们的VUE项目。
vue init webpack spa
在上面的命令中,我们说明一下webpack
和spa
这两个参数.
Webpack是指你想用哪个脚手架模板,这里其实用很多模板供我们选择,比如简化版webpack-simple
,具体使用方法可以参考这里
SPA指的是你要把项目放在哪个文件夹里,这里就是SPA这个文件夹,如果没有系统会自动创建。
运行上面这个命令后,会有一些选项让你选择,比如项目的名字等等,全部默认就可以。
运行完上面的命令后,我们需要将当前路径改变到SPA这个文件夹内,然后安装需要的模块:
//改变路径到spa文件夹下 cd spa //安装所有项目需要的npm模块 npm install //在开发环境下运行程序 npm run dev
当上面的命令运行起来的时候,用浏览器访问 http://localhost:8080 这个地址,我们会看到如下页面:
VUE Router 的安装与配置
在我们正式开始配置我们的单页程序之前,还有一个东西必须安装,vue-router。
vue-router 是VUE官方提供的一个路由组件,专门用来构建VUE的单页程序,它包含如下一些特点:
支持嵌套路由
组件化路由配置
支持路由参数传递,支持通配符
比传统路由更加优化的导航控制
自动激活不同CSS样式
支持HTML5历史模式或哈希模式,支持IE9的自动回退功能
支持自定义的滚动轴特性
如果你之前接触过angular router
或者react-router
,其实vue-router
跟这两个非常相似。
我们使用vue-router
的原因就是它可以使我们的页面在前端就完成切换,而不是每次切换页面都要向服务器重新请求数据,重载页面。
换句话说,一个单页程序就像是一个安装在本地APP一样,使用起来比传统的网页更加的流畅。
我们可以用下面的命令安装vue-router
.
npm install vue-router --save
现在让我们打开 src/main.js 文件夹把路由模块配置到我们的程序里.
复制下面的内容并替换原来的 src/main.js:
// 导入vue实例 import Vue from 'vue' //导入 App 组件 import App from './App' //导入 vue router import VueRouter from 'vue-router' //告诉vue使用vue-router路由组件 Vue.use(VueRouter) //定义路由表 const routes = [] // 创建路由器实例,并且传入`routes`变量作为路由。 // 你还可以传入别的参数,不过在这里尽量简单化就可以了 const router = new VueRouter({ routes, mode: 'history' }) //实例化Vue实例 new Vue({ //定义Vue绑定的跟元素 el: '#app', //用<App/>代替根元素 template: '<App/>', //声明App组件,这样上面的<App/>元素就可以生效 components: { App }, //将上面声明的路由器传递到根Vue实例 router }).$mount('#app')//将这个实例挂载到id=app的根元素上
让我们屡一下上面这一段代码,首先我们从node模块中导入了vue模块,接着我们又导入了App模块(这个是本地定义的模块)。
这个App模块是我们在用vue cli脚手架创建这个项目的时候默认帮我们创建的,作为我们整个项目的根模块。
之后,我们导入了vue-router, 接着我们使用vue的静态方法Vue.use,告诉vue我们将使用vue-router这个组件。
在下一行,我们定义了一个空的routes数组变量。每一个routes里面的元素为一条路由信息。当前里面还没有路由信息,后续会添加。
NOTE:注意英文中routes和router的区别,router是一个组件,route是一条路由信息,routes是由多个路由信息(route)组成的路由表。
可以这么理解,把router当成一个工人,这个工人(router)拿到一张由很多路由信息(route)组成的路由表(routes),然后每一次用户请求页面变化时,工人(router)会逐条比对路由表(routes)上的路由信息(route),遇到第一条匹配的信息就将对应的页面反馈给用户
接下来我们做的是创建我们的路由器(router),一个vue-router的实例,在这里我们给它传入了两个参数。
第一个参数是一个我们之前声明过的数组变量,路由表(routers),另一个参数在声明一种路由模式,这种模式下我们在URL中不会看到前导的#
号。
有兴趣的读者可以试试不加这个参数观察一下URL有什么变化。同时,去掉这个#
号也会使SEO优化更加好。
最后,我们创建了一个Vue实例,传入了根组件<App/>
,声明了Vue的挂载点。
现在,如果我们重新运行这个程序,会发现页面没有任何变化。下面我们将会开始设置路由。
我将会把路由管道标签(<router-view></router-view>
)加入到根组件<App/>
中,也就是当url符合某个路由信息(route)时,对应的组件就会被渲染(render)到这个路由管道标签内。
现在打开src/App.vue文件,也就是根组件文件,用下面的内容替换原有的内容:
<template> <div id="app"> <!--路由管道标签,任何符合某一路由(route)信息的组件都会在这个标签内展示出来 --> <router-view></router-view> </div> </template> <script> export default { name: 'app', } </script> <!-- css格式 --> <style> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
上面的代码和用vue-cli自动生成的代码有几点不一样:
多了 router-view 标签用来渲染符合路由条件的组件
删除了导入hello组件的语句
import hello
删除了
script
中关于component的定义删除了默认的component是因为我们不需要了,因为我们在上面已经定义了路由管道标签
<router-view>
这时候我们在重新运行程序,会发现是空白页面
路由初始化
这时候我们需要定义Hello
模块作为我们的Home页面,然后把相应的路由信息(route)加到路由表(routes)中
打开main.js
文件,把routes
变量修改成如下形式并导入Hello组件:
//导入hello组件 import Hello from './components/Hello' //定义路由表 const routes = [ //将根URL加入到路由表并声明对应Hello组件. { path: '/', component: Hello } ]
在上面的代码中我们引入了脚手架默认添加的Hello组件,并且将根URL /
对应到了Hello组件上,这时候如果我们重新运行程序,会像下面这样:
上面这个图片里,Vue的logo已经不见了,这是因为之前在APP
组件中已经将显示VUE logo图标的代码删除了。
现在,让我们定义更多的路由,在这之前先再创建一个组件。
在 src/somponents 文件夹下创建一个 About.vue 文件,然后把下面的代码放进去。
<template> <div id="about"> When you have a great story about how your product or service was built to change lives, share it. The "About Us" page is a great place for it to live, too. Good stories humanize your brand, providing context and meaning for your product. What’s more, good stories are sticky -- which means people are more likely to connect with them and pass them on. </div> </template> <script> export default { name: 'about' } </script> <style> #about { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
让我们来看看上面这个About组件,这个组件里面只有简单的一段话,当URL正好对应这个组件的时候,它就会被渲染到<router-view>
标签里面。
在这之前,我们需要在路由表中加入这个组件对应的路由。
打开 main.js 然后把路由表(routes)改成如下形式。
//导入Hello组件 import Hello from './components/Hello' //导入Aboiut组件 import About from './components/About' //定义路由表 const routes = [ //对应Hello组件的路由地址 { path: '/', component: Hello }, //对应About组件的路由地址 { path: '/about', component: About } ]
上面代码唯一的改动就是增加了About组件的引入和对应的路由信息。
这时候如果我们在浏览器中访问 http://localhost:8080/about 这个地址,我们就会看到刚刚我们在About组件中写的那段话。
在页面中使用<router-link>
标签
但是我们不能总是手工在浏览器中输入地址去切换页面,所以我们需要用到另一个路由器标签<router-link></router-link>
。
打开 App.vue 文件,然后在<router-view>
标签上面加入两个<router-link>
标签:
<router-link v-bind:to="'/'">Home</router-link> <router-link v-bind:to="'/about'">About</router-link>
上面两个标签的作用就是创建两个指向对应URL的超链接,用来做动态的路由转换 (不用重载页面的页面转换)。
这时候如果你重新启动程序,会注意到页面上多了两个可以点击的超链接,点击任意一个,页面和URL也会随着变动,而且页面并没有重载。
程序看起来是这个样子的:
总结
好了,这就是如何构建一个单页程序(SPA)。
当然,这个程序也可以构建的更加复杂一些,比如:
添加更多的路由
通过路由传递参数信息
使用路由守护(route guards)来在保证没有权限的用户不能访问特定页面
在下一个教程中,我会分别介绍路由的参数传递和路由守护功能。
在上一章中,我们学习了用VUE2构建简单的单页应用 (SPA)。
在本章中,我们将要学习:
通过路由传递参数
使用路由守卫 (route guards)使未授权的用户不能访问某些特殊页面
我会在上一章写好的程序上继续添加功能,你可以直接在我的github上克隆上一章的源代码
预览
我们会在上一章已经写好的程序上加入路由参数传递和路由守护两个功能。
通过路由传递参数
在VUE中,我们可以通过在某条路由信息 (route,希望你们还能记得route和router和routes的区别,我在上一章说过的) 中添加类似 :id
的属性来传递参数。
这种路由参数传递机制在Laravel也有。
每一条路由信息都由一对大括号括起来,就像下面这样
{ path: '/user/:id', component: User }
像上面这条路由信息,如果我在地址栏输入/users/2
,那么在目的组件中 (在这里就是User组件),我们就可以通过$route.params.id
来得到2这个整数。
其实就是把2赋值给了一个变量id, 而这个变量id则定义在$route.params.id
中。
下面来实际操作一下。
创建一个带参数的路由
在 scr\components
文件夹下创建新的组件文件Param.vue
,并且填入下面的内容:
<!--src\components\Param.vue--> <template> <div class="param"> <input type="text" size="30" v-model="UserInput" /> <input type="button" value="Go to route" @click="GoToRoute()"> </div> </template> <script> export default { name: 'Param', data () { return { UserInput :'' } }, methods:{ GoToRoute : function(){ this.$router.push({ name: 'Paramdetails', params: { id: this.UserInput }}) } } } </script>
上面这段代码我们定义了一个 param
组件, 这个组建中有一个 input
输入框和一个导航按钮,这个导航按钮会触发 GoToRoute
函数。
接着我们打开main.js
文件,把上面的Param.vue
组件导入,并且创建一个与之对应的路由信息。
就在 Const routes
语句上面,加上下面这句话:
//main.js import Param from './componets/Param'
然后把 routes
替换成如下内容:
//main.js //定义路由表 const routes = [ //主页路由 { path: '/', component: Hello }, //about页面路由 { path: '/about', component: About }, //param页面路由 { path: '/param', component: Param } ]
可以注意到上面这个代码块中,我仅仅是添加了一句话,一句param的路由信息。
在 App.vue
文件中加入对应 Param
模块的超链接点击 <router-link></router-link>
,就在 About
模块的超链接点击之后。
<!--src\components\App.vue--> <router-link v-bind:to="'/param'">Param Link</router-link>
现在运行程序,我们的主页应该像是下面这个样子:
现在紧接着 "about" 之后就是 param link
的超链接按钮,点击它,你会看到下面的页面
点击F12进入开发者工具,然后点击 Go to route
的按钮,在开发者工具栏中会出现下面的错误
[vue-router] Route with name 'Paramdetails' does not exist
这是因为当我们点击 Go to route
按钮的时候触发了 this.$router.push({name: 'Paramdetails', params: { id: this.UserInput }})
这个方法,但是我们还没有定义任何关于 paramdetails
的路由。
所以下一步,我们就要创建一个关于 paramdetails
的组件和与之对应的路由信息。
在 src/components
文件夹中创建 paramedetails.vue
文件,然后把下面的内容粘贴进去。
<!--src/components/paramdetails.vue--> <template> <div class="paramdetails"> <span>The paremter value that was passed to me is: {{ $route.params.id }}</span> </div> </template> <script> export default { name: 'paramdetails' } </script>
这个组件唯一的作用的就是把路由传过来的参数显示在页面上。
接着,在 main.js
中引入这个组件,就在 Const routes
这句话上面写:
//main.js //import paramdetails component import paramdetails from './components/paramdetails'
把 routes
替换成下面的内容:
//main.js //定义路由表 const routes = [ { path: '/', component: Hello }, { path: '/about', component: About }, { path: '/param', component: Param }, //定义可以传参的路由信息 { path: '/Paramdetails/:id', component: paramdetails, name: 'Paramdetails' } ]
上面这段代码加入了对 paramdetails
组件的路由信息,并且路由信息中加入对参数 :id
的引用。
另外,还加入了另外一个参数 name
, 关于 name
参数,其实它是为了我们更加方便的记忆,这样在切换路由的时候就不需要输入完整的URL地址,取而代之用 name
定义的名字就可以正确导航到对应的路由。
可以回过头看看上面我们是怎么定义 GoToRoute
这个方法的,在 param.vue
文件中。
我们在 this.$router.push({name: 'Paramdetails', params: { id: this.UserInput }})
中就通过使用 name
参数来简化路由的导航。
现在,我们重新运行程序,点击 param link
导航到 param
组件,然后在输入框中输入52, 接着点击 go to route
按钮, 你将会看到下面的页面:
好了!你已经成功的通过路由把参数传递过来了,恭喜!
使用路由守护 (route guards)
路由守护我更愿意把他看成是一种钩子方法,简单说就是他可以在路由没有跳转之前 (before) 和已经跳转之后 (after) 分别执行一些动作 (action)。
路由守护分为全局守护和组件守护两种,顾名思义,全局守护会检查每一次路由跳转,组件守护只会在当前组件内部出现路由跳转时触发。
下面我们将在 main.js
文件中添加全局路由守护 (beforeEach)。
打开 main.js
文件, 紧挨着 routes
常量下面加入下面的内容:
//main.js //设置路由守护 router.beforeEach((to, from, next) => { //检查这个跳转是否是要跳转到 param 组件 if(to.path == '/param'){ //如果'user' 对象还未赋值 if(localStorage.getItem('user')==undefined){ //提示输入用户名 var user = prompt('please enter your username'); //提示输入密码 var pass = prompt('please enter your password'); //检查用户输入的用户名密码是否和我们预设的一样 if (user == 'username' && pass == 'password'){ //赋值user对象 localStorage.setItem('user', user); //跳转到对应的组件 next(); }else{// //如果用户名密码不正确则显示错误信息 alert('Wrong username and password, you do not have permission to access that route'); //返回并且不进行路由跳转 return; } } } //如果要跳转的路径不是 param 组件,则不进行检查直接跳转 next() })
上面的 beforeEach
方法有三个参数, to
参数代表跳转目的地对象, 而 from
这是代表原目的地对象。
在 beforeEach
方法中,首先检查 "to.path" 是否等于 /param
, 如果是, 则检查 "localStorage" 数据中是否已经存有用户 (user) 的信息。
如果没有,则弹出提示框要求输入用户名和密码,如果正确就创建 "localStorage" 本地存储,用户下次再访问同样的页面就别用重复输入用户信息。
NOTE: next 方法用来跳转到用户将要跳转的页面,是一个必须调用的方法。 如果不调用此方法,那么用户将不能进行路由跳转。所以在上面的例子中如果用户输入错误,则直接警告并且返回,没有调用 next 方法。上面只是一个最简单的例子,只能作为理解使用,千万不要应用到实际开发中。
总结
现在你应该已经学会了:
通过路由传递参数
路由名称 (name) 的运用和创建
路由守护的使用
对了,与 beforeEach
方法相对应的是 afterEach
方法,相信聪明的你肯定能举一反三。
再加上上一章学会的注册路由,所有这一切知识点加起来,我相信,你已经可以独立完成一个不错的单页程序 (SPA) 了!
链接:https://www.jianshu.com/p/47a207618cbd