Vue服务端渲染,解决关键css分离失败

vue2 中以下配置,打包出来资源,html 没有把页面中 vue 文件中 css,放到头部

1
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
{
"name": "******.com",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"test:unit": "vue-cli-service test:unit"
},
"dependencies": {
"async": "^3.2.0",
"axios": "^0.21.1",
"core-js": "^3.6.5",
"crypto-js": "^4.1.1",
"echarts": "^5.1.2",
"element-ui": "2.15.8",
"heapdump": "^0.3.15",
"jquery": "^3.6.0",
"js-cookie": "^3.0.1",
"lru-cache": "^6.0.0",
"memory-cache": "^0.2.0",
"request": "^2.88.2",
"swiper": "^5.4.5",
"video.js": "^7.13.3",
"vue": "2.6.11",
"vue-awesome-swiper": "^4.1.1",
"vue-meta": "^2.4.0",
"vue-no-ssr": "^1.1.1",
"vue-router": "^3.2.0",
"vue-server-renderer": "2.6.11",
"vue-template-compiler": "2.6.11",
"vuex": "^3.4.0",
"vuex-router-sync": "^5.0.0",
"weixin-js-sdk": "^1.6.0"
},
"devDependencies": {
"@babel/preset-env": "^7.14.7",
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-router": "~4.5.0",
"@vue/cli-plugin-unit-mocha": "~4.5.0",
"@vue/cli-plugin-vuex": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/test-utils": "^1.0.3",
"babel-core": "^6.26.3",
"babel-loader": "^8.2.2",
"babel-plugin-component": "^1.1.1",
"babel-plugin-import": "^1.13.3",
"babel-polyfill": "^6.26.0",
"babel-register": "^6.26.0",
"chai": "^4.1.2",
"concurrently": "^6.2.0",
"cross-env": "^7.0.3",
"cssnano": "^5.0.6",
"cssnano-preset-advanced": "^5.1.3",
"dns": "^0.2.2",
"dotenv": "^8.6.0",
"extract-text-webpack-plugin": "^4.0.0-beta.0",
"ioredis": "^5.3.2",
"koa": "^2.13.1",
"koa-bodyparser": "^4.3.0",
"koa-cors": "^0.0.16",
"koa-log4": "^2.3.2",
"koa-logger": "^3.2.1",
"koa-mount": "^4.0.0",
"koa-proxies": "^0.12.1",
"koa-router": "^10.0.0",
"koa-static": "^5.0.0",
"koa-static-cache": "^5.1.4",
"koa2-proxy-middleware": "0.0.4",
"lodash.merge": "^4.6.2",
"memory-fs": "^0.5.0",
"node-sass": "^4.12.0",
"nodemon": "^2.0.7",
"pm2": "^5.3.0",
"postcss": "^8.0.0",
"postcss-import": "^12.0.0",
"postcss-preset-env": "^6.7.0",
"postcss-scss": "^3.0.4",
"postcss-url": "^8.0.0",
"postcss-write-svg": "^3.0.1",
"rate-limiter-flexible": "^2.4.1",
"redis": "^3.1.2",
"redis-json": "^6.0.3",
"sass": "^1.26.10",
"sass-loader": "^8.0.2",
"shelljs": "^0.8.5",
"sw-precache-webpack-plugin": "^1.0.0",
"terser-webpack-plugin": "^4.0.0",
"vue-loader": "^15.9.7",
"vue-skeleton-webpack-plugin": "^1.2.2",
"vue-template-compiler": "2.6.11",
"vue-template-loader": "^1.1.0",
"webpack-bundle-analyzer": "^4.4.2",
"webpack-node-externals": "^3.0.0",
"webpackbar": "^5.0.0-3"
},
"eslintConfig": {
"root": true,
"env": {
"es6": true,
"node": true,
"browser": true
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}

原因分析

直接通过原始 webpack 配置是可以的;但是通过 vue 的配置就不行;怀疑是 vue 配置某个地方冲突;
通过打印配置
./node_modules/@vue/cli-service/bin/vue-cli-service.js inspect > log.txt

解决方式

最终测试,发现是 rule 中 vue 的配置有问题,把 cache 去掉就好,修改如下:

1
2
3
4
5
6
chainWebpack: (config) => {
// 修复某些机器打包,ssr渲染没有提取关键css资源
const vueRule = config.module.rule('vue')
vueRule.uses.clear()
vueRule.use('vue-loader').loader('vue-loader')
}

配置修改之前:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* config.module.rule('vue') */
{
test: /\.vue$/,
use: [
{
loader: '/Users/hzz/Documents/JFZ/new.jfz.com2/node_modules/cache-loader/dist/cjs.js',
options: {
cacheDirectory: '/Users/hzz/Documents/JFZ/new.jfz.com2/node_modules/.cache/vue-loader',
cacheIdentifier: '53c04142'
}
},
{
loader: '/Users/hzz/Documents/JFZ/new.jfz.com2/node_modules/vue-loader/lib/index.js'
}
]
},

配置修改之后:

1
2
3
4
5
6
7
8
9
/* config.module.rule('vue') */
{
test: /\.vue$/,
use: [
{
loader: 'vue-loader'
}
]
},

参考

  1. css-loader 导致 vue 中样式失效
    https://www.cnblogs.com/tudou1179006580/p/14347787.html
  2. VUE SSR 基础功能搭建
    https://juejin.cn/post/7067836641947172872
  3. Vue SSR 指南(Vue.js 服务器端渲染指南)
    https://www.bookstack.cn/read/vue-ssr-zh/css.md

vuex常用注意事项

  • getter会缓存结果,不需要缓存,需要把getter变为函数

    1
    2
    3
    4
    5
    getters: {
    getTodoById: (state) => (id) => {
    return state.todos.find(todo => todo.id === id)
    }
    }

    每次调用为

    1
    store.getters.getTodoById(1)

    这样通过函数触发,不会缓存每次结果

  • dispatch和commit区别
    dispatch为异步执行,会返回promise
    commit为同步执行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    actions: {
    async actionA ({ commit }) {
    commit('gotData', await getData())
    },
    async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // 等待 actionA 完成
    commit('gotOtherData', await getOtherData())
    }
    }

    store.dispatch('actionA').then(() => {
    // ...
    })

    一个 store.dispatch 在不同模块中可以触发多个 action 函数。在这种情况下,只有当所有触发函数完成后,返回的 Promise 才会执行。