作者 | 刘汉伟
关于本书
本书从Vue框架的基础语法讲起,逐步深入Vue进阶实战,并在最后配合项目实战案例,重点演示了Vue在项目开发中的一些应用。在系统地讲解Vue的相关知识之余,本书力图使读者对Vue项目开发产生更深入的理解。
本书共分为11章,涵盖的主要内容有前端的发展历程、Vue的基本介绍、Vue的语法、Vue中的选项、Vue中的内置组件、Vue项目化、使用Vue开发电商类网站、使用Vue开发企业官网、使用Vue开发移动端资讯类网站、使用Vue开发工具类网站。
本书内容通俗易懂、案例丰富、实用性强,特别适合Vue的初学者和从业人员阅读,同时也适合职业生涯遇到“瓶颈”的前端从业人员和其他编程爱好者阅读。另外,本书也适合作为相关培训机构的教材。
关于作者
刘汉伟,一线软件工程师,活跃于GitHub等开源社区,接触过许多优秀项目的代码,有丰富的专业知识和开发经验,曾负责并上线多个大型Web项目。2016年起开始在网上发表博客,短时间内各平台累计访问量超过10万人次,深受读者好评,并受邀成为CSDN专业讲师、腾讯云+社区专栏作者。
关于活动规则
这是五一第一次给大家送书,规则非常简单,只要大家在【有课网】(【有课网】与【web前端开发】是两个兄弟号,欢迎大家关注我的新号【有课网】)的公众号评论区分享一下,你为啥想要这本《Vue.js从入门到项目实战》或者你在开发中与vue的各种有趣故事,只要你的评论点赞数在前7名即可获得图书一本。
开奖时间:2019-05-02 早上9点,中奖名单也将在【有课网】进行公布,请继续关注。
长按二位码关注【有课网】
以下内容摘自于---《Vue.js从入门到项目实战》一书中
内容如下:
路由这个概念首先出现在后台。传统MVC架构的web开发,由后台设置路由规则,当用户发送请求时,后台根据设定的路由规则将数据渲染到模板中,并将模板返回给用户。因此,用户每进行一次请求就要刷新一次页面,十分影响交互体验。
ajax的出现则有效解决了这一问题。ajax(asynchronous javascript and xml),浏览器提供的一种技术方案,采用异步加载数据的方式以实现页面局部刷新,极大提升了用户体验。
而异步交互体验的更高版本就是SPA——单页应用,不仅页面交互无刷新,甚至页面跳转之间也可以无刷新,前端路由随之应运而生。
一、前端路由的简单实现
广义上的前端路由是由前端根据URL来分发视图,其实现有两个核心操作:一是需要监听浏览器地址的变化,二是需要动态加载视图。
笔者分别使用Vue和原生的JS来模拟其实现,并用到Node.js创建服务端文件,服务端文件app.js的代码如下:
const http = require('http') // http模块
const fs = require('fs') // 文件处理模块
const hostName = '127.0.0.1'
const port = 3000
const server = http.createServer(function(req, res) { // 创建http服务
letcontent = fs.readFileSync('index.html') // 读取文件
res.writeHead(200, { // 设置响应内容类型
'content-type': 'text/html;charset="utf-8"'
})
res.write(content) // 返回index.html文件内容
res.end()
})
server.listen(port, hostName, function () {// 启动服务监听
console.log(`Server is running here: http://${hostName}:${port}`)
})
这段代码用到了Node的http和fs模块,用以创建一个可以返回index.html页面的服务。想要启动服务,首先要到Node官网下载安装Node客户端(推荐使用8.11.3长期稳定版),之后在文件所处目录下输入命令:
node app.js // node + 文件名
当控制台显示“Server is runninghere:http://127.0.0.1:3000”时,即表示服务启动成功。
下面来看一下使用Vue实现前端路由的代码(index.html):
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<div id="app">
<ul>
<li><router-linkto="/">Home</router-link></li>
<li><router-linkto="/about">About</router-link></li>
</ul>
<router-view></router-view>
</div>
<script type="text/javascript">
letHome = {
template: '<h1>This is Home!</h1>'
}
letAbout = {
template: '<h1>This is About!</h1>'
}
letroutes = [ // 定义路由规则
{
path: '/',
component: Home
},
{
path: '/about',
component: About
}
]
letRouterLink = {
props: ['to'],
template: '<a :href="to"><slotname="default"></slot></a>'
}
letRouterView = {
data () {
return {
url: window.location.pathname // 获取浏览器地址
}
},
computed: {
ViewComponent () { // 根据浏览器地址返回相应组件
return routes.find(route => route.path === this.url).component
}
},
render (h) {
return h(this.ViewComponent)
}
}
/*eslint-disable */
letvm = new Vue({
el:'#app',
components: { RouterLink, RouterView }
})
</script>
在这段代码中,笔者声明了Home、About、RouterLink和RouterView四个组件。Home和About为待分发的视图组件;RouterLink为触发视图切换的组件;RouterView为挂载动态视图的组件。之后,笔者在vm实例中通过监测window.location.pathname的变化来动态分发视图。
这种方式虽然实现了前端路由,但其实视图切换还是由页面刷新来执行的,这并不是一个单页应用。
使用原生JS实现前端路由的代码如下(index.html):
<div>
<ul>
<li><a href="#/">Home</a></li>
<li><a href="#/about">About</a></li>
</ul>
<!-- 动态视图被挂载的元素 -->
<div id="view"></div>
</div>
<script type="text/javascript">
letHome = '<h1>This is Home!</h1>' // 视图模板 Home
letAbout = '<h1>This is About!</h1>' // 视图模板 About
letRouter = function (el) { // 定义路由类
letview = document.getElementById(el)
letroutes = [] // 路由规则列表
letload = function (route) { // 加载视图
route && (view.innerHTML = route.template)
}
letredirect = function () { // 分发视图
let url = window.location.hash.slice(1) || '/'
for (let route of routes) {
url === route.url && load(route)
}
}
this.push = function (route) { // 添加路由规则
routes.push(route)
}
window.addEventListener('load', redirect, false) // 页面加载时
window.addEventListener('hashchange', redirect, false) // URL变化时
}
letrouter = new Router('view') // 实例化路由
router.push({ // 添加路由规则
url: '/',
template: Home
})
router.push({
url: '/about',
template: About
})
</script>
在这段代码中,笔者为浏览器的内置对象window在页面加载和URL变化时添加了监听器以分发视图。细心的同学可以发现,笔者在a标签的href中写入了“#”符号,这个符号可以阻止页面刷新(实现了单页应用),但也会在URL中加入该符号,因此笔者在redirect函数中并没有直接取window.location.hash的值,而是先slice(1),将“#”去掉。
两种实现的运行结果的初始页面均如图1所示。
图1 前端路由的简单实现(1)
当点击About链接时,页面如图2所示。
图2 前端路由的简单实现(2)
虽 然两种实现的视图表现看似相同,但其实效果却大相径庭,同学们可以结合实例体会一下。
二、Vue中的前端路由
Vue Router是Vue.js官方提供的路由管理器,它与Vue.js的核心深度集成,且随着Vue.js版本更新而更新,致力于简化单页应用的构建。
Vue Router的功能十分丰富且强大,笔者无意于枚举一些抽象的概念,下面笔者将通过几个简单的示例(需要运行在服务端上)来演示一下它的用法。
1.基础路由
<scriptsrc="https://unpkg.com/vue/dist/vue.js"></script>
<scriptsrc="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<ul>
<li><router-linkto="/">Home</router-link></li>
<li><router-linkto="/about">About</router-link></li>
</ul>
<router-view></router-view>
</div>
<scripttype="text/javascript">
letHome = { template: '<h1>This is Home!</h1>' } // Home组件
letAbout = { template: '<h1>This is About!</h1>' } // About组件
letroutes = [ // 定义路由规则, 每一个路由规则应该映射一个视图组件
{path: '/', component: Home },
{path: '/about', component: About }
]
letrouter = new VueRouter({ // 创建VueRouter实例, 并传入routes配置
routes
})
letapp = new Vue({
router
}).$mount('#app')
</script>
RouterLink和RouterView是Vue Router提供的两个内置组件。RouterLink默认会被渲染成一个<a>标签,其to属性用于指定跳转链接;RouterView将负责挂载路由匹配到的视图组件。
2.动态路由
<scriptsrc="https://unpkg.com/vue/dist/vue.js"></script>
<scriptsrc="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<divid="app">
<ul>
<li><router-linkto="/">Home</router-link></li>
<li @click="add">
<!-- 2. 参数num由实例传入路由 -->
<router-link :to="'/about/' +num">About</router-link>
</li>
</ul>
<router-view></router-view>
</div>
<scripttype="text/javascript">
let Home = { template: '<h1>This isHome!</h1>' } // Home组件
let About = { // About组件
template: '<div>' +
'<h1>This is About!</h1>' +
'<p>num: {{ $route.params.num}}</p>' + // 3. 在组件中显示参数 num
'</div>'
}
let routes = [ // 定义路由规则, 每一个路由规则应该映射一个视图组件
{ path: '/', component: Home },
{ path: '/about/:num', component: About }// 1. 定义了参数 num, 格式如 /:num
]
let router = new VueRouter({ // 创建VueRouter实例, 并传入routes配置
routes
})
let app = new Vue({
data () {
return { num: 0 }
},
methods: { // 当点击About时, num值自增1
add () { this.num++ }
},
router
}).$mount('#app')
</script>
我们可以使用动态路由参数将匹配某种模式的所有路由映射到同一个组件(致敬RESTful)。如上述示例中,Vue Router将所有匹配/about/:num的路径全都映射到About组件中(图3),并将num作为组件中的一个参数。
图3 动态路径路由
路径参数应用冒号“:”标记,但是在使用时应注意设计的规则是否合理,比如:
routes:[
{ path: '/:any', component: Home} // 可以匹配路径为/about的路由,’about’将作为any的值
]
将会把所有路径都匹配到Home组件中。
当动态路径被匹配时,我们可以在组件中使用this.$route.params来获取参数的值。
3.嵌套路由
<scriptsrc="https://unpkg.com/vue/dist/vue.js"></script>
<scriptsrc="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<divid="app">
<ul>
<li><router-linkto="/">Home</router-link></li>
<li>
<div><router-linkto="/about">About</router-link></div>
<ul>
<!-- 3. 使用嵌套路由 -->
<li><router-linkto="/about/author">About - Author</router-link></li>
<li><router-link to="/about/email">About- Email</router-link></li>
</ul>
</li>
</ul>
<router-view></router-view>
</div>
<scripttype="text/javascript">
let Home = { template: '<h1>This isHome!</h1>' } // Home组件
let About = { // About组件
template: '<div>' +
'<h1>This is About!</h1>' +
'<router-view></router-view>'+ // 1. 嵌套的动态视图区
'</div>'
}
let Author = { template: '<p>Author:lonelydawn</p>' } // Author组件
let Email = { template: '<p>Email:lonelydawn@sina.com</p>' } // Email组件
letroutes = [ // 定义路由规则, 每一个路由规则应该映射一个视图组件
{ path: '/', component: Home },
{
path: '/about',
component: About,
children: [ // 2. 嵌套子路由
{ path: 'author', component: Author },
{ path: 'email', component: Email }
]
}
]
let router = new VueRouter({ // 创建VueRouter实例, 并传入routes配置
routes
})
let app = new Vue({
router
}).$mount('#app')
</script>
嵌套路由可以实现在动态视图中嵌套动态视图。
这里有个问题,多层的动态视图是否可以使用Vue的内置组件component来实现呢?当然可以。不过使用component切换的视图会在页面刷新后回到初始状态,而使用路由分发的视图在页面刷新后会保持当前路径对应的视图,并在浏览器的history中留下记录。
4.编程式路由
<scriptsrc="https://unpkg.com/vue/dist/vue.js"></script>
<scriptsrc="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<divid="app">
<ul>
<!-- 默认字符串为路径参数 -->
<li@click="redirectByPath('/')">Home</li>
<li>
<!-- 指定参数为路径 -->
<div@click="redirectByPath('/about')">About</div>
<ul>
<!-- 嵌套路由-->
<li@click="redirectByPath('/about/author')">About - Author</li>
<!-- 嵌套路由, 动态路由, 当使用path时, params参数不生效 -->
<li@click="redirectByPath('/about/email', { email: lonelydawn@sina.com'})">About - Email</li>
<!-- 嵌套路由, 动态路由, 可以直接将参数写入path -->
<li@click="redirectByPath('/about/email/lonelydawn@sina.com')">About- Email</li>
<!-- 嵌套路由, 动态路由, 使用命名路由跳转视图 -->
<li@click="redirectByName('Email', { email: 'singledawn@sina.com'})">About - Email</li>
</ul>
</li>
</ul>
<router-view></router-view>
</div>
<scripttype="text/javascript">
let Home = { template: '<h1>This isHome!</h1>' } // Home组件
let About = { // About组件
template: '<div>' +
'<h1>This is About!</h1>' +
'<router-view></router-view>'+ // 嵌套的动态视图区
'</div>'
}
let Author = { template: '<p>Author:lonelydawn</p>' }
let Email = { template: '<p>Email: {{$route.params.email }}</p>' }
let routes = [ // 定义路由规则, 每一个路由规则应该映射一个视图组件
{ path: '/', component: Home },
{
path: '/about',
component: About,
children: [ // 嵌套子路由
{ name: 'Author', path: 'author',component: Author },
{ name: 'Email', path: 'email/:email',component: Email }
]
}
]
let router = new VueRouter({ // 创建VueRouter实例, 并传入routes配置
routes
})
let app = new Vue({
methods: {
redirectByPath (path, params) {
this.$router.push({ path, params })
},
redirectByName (name, params) {
this.$router.push({ name, params })
}
},
router
}).$mount('#app')
</script>
这里并没有使用RouterLink组件,而是在JS中使用router.push方法跳转视图。
我们可以通过路由的path跳转视图,还可以赋予路由name属性,然后通过name跳转视图。
动态参数应放在params中,当使用path时,params参数不生效,此时应将参数值直接写进path中。
5.使用Vue Cli快速构建的项目中的router/index.js
importVue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}
]
})
这里使用Vue.use安装Vue Router插件。
这里使用export返回路由规则。默认只有当路径为/时,渲染HelloWorld组件。
关于Vue Router的知识点有很多,笔者在这里并没有一一列举,而是介绍了其最常见的几种用法。实际上,掌握这些已经能在实战中应对大部分的情况了。