webpack-target
webpack可以为js的各种不同的宿主环境提供编译功能,为了能正确的进行编译,就需要开发人员在配置里面正确的进行配置。
默认情况下,target的值是web,也就是为类浏览器的环境提供编译。1
2
3module.exports = {
target: 'node'
};
webpack-vue-ssr-webpack-plugin
当您使用Webpack的按需代码分割功能(通过require.ensure或动态import)时,生成的服务器端捆绑包将包含多个单独的文件。此插件通过自动将这些文件打包到可传递到的单个JSON文件中来简化工作流程bundleRenderer。
node-writeFileSync
同步,既当写入完成后才进行下一步,而且会把文件里的内容覆盖为新内容。1
2const fs = require('fs');
fs.writeFileSync(filename, data, [options])
html-minifier
作用:压缩的文件
第一个参数
String类型, 一段html代码
第二个参数 options
这里只列了几个常用的
removeComments 默认值false;是否去掉注释
collapseWhitespace 默认值false;是否去掉空格
minifyJS 默认值false;是否压缩html里的js(使用uglify-js进行的压缩)
minifyCSS 默认值false;是否压缩html里的css(使用clean-css进行的压缩)1
2
3
4html = htmlMinifier.minify(html, {
collapseWhitespace: true,
minifyCSS: true,
});
vue-server-renderer
用于 Vue2.0 的服务端渲染,既可以在node环境下把所需的内容写入html等目标文件里,实现服务器渲染功能。
插件方式
会打包成json文件1
2
3
4
5
6
7const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')
或
const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')
const plugin = new VueSSRServerPlugin({
filename: 'my-server-bundle.json' // 自定义名称
})
createBundleRenderer
使用预编译的应用程序包创建bundleRenderer。bundle参数可以是以下之一:
生成的bundle文件(.js或.json)的绝对路径。文件路径必须以/开头
由vue-ssr-webpack-plugin生成的束对象。1
2const { createBundleRenderer } = require('vue-server-renderer')
const bundleRenderer = createBundleRenderer(serverBundle, { /* options */ })
renderToString
将捆绑的应用程序复制到字符串。与renderer.renderToString相同的回调。可选的上下文对象将被传递到bundle的导出函数。1
2
3bundleRenderer.renderToString({ url: '/' }, (err, html) => {
// ...
})
注意:该HTML必须包含, 这个createBundleRenderer函数把这行代码替换成HTML.
由于webpack3与webpack4的vue-loader的差别,webpack4可以使用vue-loader15以上,所以要安装插件VueLoaderPlugin,而vue-loader14既以下都不需要这个。
问题1
2
3
4
5
6
7
8Vue packages version mismatch:
- vue@2.4.4
- vue-template-compiler@2.5.13
This may cause things to work incorrectly. Make sure to use the same version for
both.
If you are using vue-loader@>=10.0, simply update vue-template-compiler.
If you are using vue-loader@<10.0 or vueify, re-installing vue-loader/vueify sho
uld bump vue-template-compiler to the latest.
解决:1
2出现这种错误之后可以使用命令,将vue的版本改成和vue-template-compiler的版本一致,使用命令
npm install vue@2.5.13 --save,然后直接运行就可以了!
完整代码
config目录下建webpack.skeleton.conf.js1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45const path = require('path');
const webpack = require('webpack');
const nodeExternals = require('webpack-node-externals');
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin');
module.exports = {
target: 'node',
entry: {
skeleton: './src/skeleton/skeleton.entry.js',
},
output: {
path: path.resolve(__dirname, '../dist'),
publicPath: '/dist/',
filename: '[name].js',
libraryTarget: 'commonjs2',
},
module: {
rules: [
{
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader',
],
},
{
test: /\.vue$/,
loader: 'vue-loader',
},
],
},
externals: nodeExternals({
whitelist: /\.css$/,
}),
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js',
},
extensions: ['*', '.js', '.vue', '.json'],
},
plugins: [
new VueSSRServerPlugin({
filename: 'skeleton.json',
}),
],
};
config目录下建skeleton.js1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28const fs = require('fs');
const { resolve } = require('path');
const htmlMinifier = require('html-minifier');
const createBundleRenderer = require('vue-server-renderer').createBundleRenderer;
// 先把vue的模板文件index.html置换成标准的模板,防止骨架屏污染
let tempData = fs.readFileSync(resolve(__dirname, '../temp.html'), 'utf-8');
fs.writeFileSync(resolve(__dirname, '../index.html'), tempData, 'utf-8');
console.log('模板注入完成');
// 读取`skeleton.json`,以`index.html`为模板写入内容
const renderer = createBundleRenderer(resolve(__dirname, '../dist/skeleton.json'), {
template: fs.readFileSync(resolve(__dirname, '../index.html'), 'utf-8'),
});
// 把上一步模板完成的内容写入(替换)`index.html`
renderer.renderToString({}, (err, html) => {
if (err) {
console.log(err);
return;
}
// 压缩文件
// html = htmlMinifier.minify(html, {
// collapseWhitespace: true,
// minifyCSS: true,
// });
fs.writeFileSync(resolve(__dirname, '../index.html'), html, 'utf-8');
});
console.log('骨架屏注入完成');
根目录(既index.html的目录)下建temp.index,而且从index.html复制到temp.index里,添加注释1
2
3
4
5
6
7
8
9
10
11
12
13
14<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>my-demo</title>
</head>
<body>
<div id="app">
<!--vue-ssr-outlet-->
</div>
<!-- built files will be auto injected -->
</body>
</html>
src目录下建seleton文件夹,里面添加seleton.vue与seleton.entry.js文件
seleton.vue1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46<template>
<div class="skeleton page">
<div class="skeleton-nav"></div>
<div class="skeleton-swiper">
<div class="skeleton-swiper-item item-one"></div>
<div class="skeleton-swiper-item item-two"></div>
</div>
</div>
</template>
<style>
html,body,div{
margin:0;
padding:0;
}
.skeleton {
height: 100%;
overflow: hidden;
box-sizing: border-box;
background: #fff;
}
.skeleton-nav {
height: 54px;
background: #eee;
margin-bottom: 20px;
}
.skeleton-swiper {
min-height:600px;
max-width:1280px;
margin:0 auto;
}
.skeleton-swiper-item{
min-height: 600px;
height:100%;
background:#eee;
border-radius:5px;
}
.item-one{
width:20%;
float:left;
}
.item-two{
width:78%;
float:right;
}
</style>
seleton.entry.js1
2
3
4
5
6
7
8
9import Vue from 'vue';
import Skeleton from './Skeleton.vue';
export default new Vue({
components: {
Skeleton,
},
template: '<skeleton />',
});