为了让页面更快完成加载, 第一时间呈现给客户端, 也为了帮助客户端节省流量资源, 我们可以开启 vue-router 提供的按需加载功能, 让客户端打开页面时, 只自动加载必要的资源文件, 当客户端操作页面, 切换功能模块, 触发页面路由变化时, 再去加载相应需要的资源.
本系列博客的前面几篇一直在讲利用 webpack + vue 开发多页面前端, 然而多页面并不是利剑所指. 本篇将重点介绍使用 vue-router 开发单页面前端, 并且实现按需加载的效果.
把它改造成按需加载的单页面.
npm i vue-router -D
修改 webpack.config.js, 改造成单页面
const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const webpack = require('webpack'); const path = require('path'); require("babel-polyfill"); function resolve(dir) { return path.resolve(__dirname, dir) } module.exports = { //定义页面的入口, 因为js中将要使用es6语法, 所以这里需要依赖 babel 垫片 entry: { index: ['babel-polyfill', './src/index.js'] }, output: { path: resolve('dist'), // 指示发布目录 chunkFilename: 'js/[name].[chunkhash:8].js', filename: 'js/[name].[chunkhash:8].js' //指示生成的页面入口js文件的目录和文件名, 中间包含8位的hash值 }, externals: { //video.js 作为外部资源引入 'video.js': 'videojs' }, //下面给一些常用组件和目录取别名, 方便在js中 import resolve: { extensions: ['.js', '.vue', '.json'], alias: { 'vue$': 'vue/dist/vue.common.js', 'jquery$': 'admin-lte/plugins/jQuery/jquery-2.2.3.min.js', 'src': resolve('src'), 'assets': resolve('src/assets'), 'components': resolve('src/components') } }, module: { //配置 webpack 加载资源的规则 rules: [{ test: /\.js$/, loader: 'babel-loader', include: [resolve('src')] }, { test: /\.vue$/, loader: 'vue-loader' }, { test: /\.css$/, loader: 'style-loader!css-loader' }, { test: /\.less$/, loader: "less-loader" }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, loader: 'url-loader?limit=10000&name=images/[name].[hash:8].[ext]' }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, loader: 'url-loader?limit=10000&name=fonts/[name].[hash:8].[ext]' }, { test: /\.(swf|mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, loader: 'url-loader?limit=10000&name=media/[name].[hash:8].[ext]' }] }, plugins: [ //引入全局变量 new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery', "window.jQuery": 'jquery', "window.$": 'jquery' }), new webpack.DllReferencePlugin({ context: __dirname, manifest: require('./dll/vendor-manifest.json') }), new CopyWebpackPlugin([ { from: 'dll', ignore: ['template.html', 'vendor-manifest.json'] } ]), //编译前先清除 dist 发布目录 new CleanWebpackPlugin(['dist']), //生成视频广场首页, 在这个页面中自动引用入口 index --> dist/js/index.[chunkhash:8].js //以 src/index.html 这个文件作为模板 new HtmlWebpackPlugin({ filename: 'index.html', title: '视频广场', inject: true, // head -> Cannot find element: #app chunks: ['index'], template: './dll/template.html', minify: { removeComments: true, collapseWhitespace: false } }) ] };
只保留一个 entry, 一个 HtmlWebpackPlugin
在 output 中添加chunkFilename 属性, 以对按需加载的 js 文件命名
新建路由文件
add src/router.js
import Vue from 'vue'
import Router from 'vue-router'
import AdminLTE from 'components/AdminLTE.vue'
import Index from 'components/Index.vue'
// import Player from 'components/Player.vue'
const Player = () => import(/* webpackChunkName: 'player' */ 'components/Player.vue')
const About = () => import(/* webpackChunkName: 'about' */ 'components/About.vue')
Vue.use(Router);
const router = new Router({
routes: [
{
path: '/',
component: AdminLTE,
children: [
{
path: '',
component: Index
}, {
path: 'player',
component: Player
}, {
path: 'about',
component: About
}
]
}
],
linkActiveClass: 'active'
})
export default router;
/ –> Inex 直接加载
/player –> Player 懒加载
/about –> About 懒加载
其中, Player 和 About 两个组件以按需加载的方式的引入, 区别于 Index, 注意文件开始部分, import 的写法, 就是 vue-router 官方文档中 懒加载 部分的写法
改造入口文件
src/index.js
import Vue from 'vue'
import store from 'src/store'
import router from 'src/router'
new Vue({
el: '#app',
store,
router,
template: `
`
})
router-view 标签用于占位路由指向的目标组件
修改 Sider 为路由导航
components/Sider.vue
<script>
export default {
props: {
menus : {
default : () => []
}
}
}
</script>
router-link 定义路由导航链接, 这里路由链接渲染为 li > a 的形式, 当前访问路径为 router-link 或者其子路由时, 该 router-link 自动触发为激活状态, 激活状态的 class name 可以通过
linkActiveClass 指定, 我们在前面的创建 router 的时候, 统一将其指定为
active
如果只想让访问路径严格匹配路由时, 触发为激活状态, 即子路由激活时, 父路由不激活, 需要在父路由上使用
exact 标识. 这个选项, 常用于首页路由
/
改造 AdminLTE.vue
将原来的 slot 占位, 替换成 router-view 占位
<script>
import "font-awesome/css/font-awesome.css"
import "admin-lte/bootstrap/css/bootstrap.css"
import "admin-lte/dist/css/AdminLTE.css"
import "admin-lte/dist/css/skins/_all-skins.css"
import "admin-lte/bootstrap/js/bootstrap.js"
import "admin-lte/dist/js/app.js"
import { mapState } from "vuex"
import Vue from 'vue'
import Sider from 'components/Sider'
import NaviBar from 'components/NaviBar'
export default {
data() {
return {
}
},
mounted() {
$(() => {
$.AdminLTE.layout.fix();
})
},
components: {
NaviBar, Sider
},
computed: {
//访问 vuex store 中的数据
//此处用到 es6 stage-2 才有的三个点展开对象的语法, 对应 .babelrc 中的配置
...mapState([
"logoText",
"logoMiniText",
"menus"
])
},
methods: {
btnClick(msg){
alert(msg);
}
}
}
</script>
运行程序, 看看效果
npm run start
如上图所示, 当首次点击 [HLS 播放器] 改变路由时, 从 Netwok 观察到加载 player.xxx.js , 表明懒加载开启成功