Skip to content

Commit

Permalink
chore: 构建优化
Browse files Browse the repository at this point in the history
  • Loading branch information
Maorey committed Nov 23, 2019
1 parent a2c9afd commit 9901cf1
Show file tree
Hide file tree
Showing 10 changed files with 666 additions and 618 deletions.
1 change: 1 addition & 0 deletions .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ VUE_CLI_BABEL_TRANSPILE_MODULES=true

# 服务主机,默认0.0.0.0(可通过localhost和ip访问), 默认打开优先ip
DEV_SERVER_HOST=0.0.0.0
# DEV_SERVER_NETWORK=IPv6
# 服务端口,默认9000
DEV_SERVER_PORT=9000
# 默认打开页 默认/
Expand Down
26 changes: 12 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ yarn vue-cli-service help # [命令] : 比如 yarn vue-cli-service help test:e2e
├── public # 静态文件目录, 除特殊文件(比如 html 模板)外, 直接复制到输出目录下
├── src # 源码目录
│ │── api # http通信
│ │ └── config # api 相关配置, 比如接口字典
│ │── assets # 静态资源文件目录, 使用到的会被解析处理(比如图片可能转成base64写入css/js或复制到输出目录)
│ │── components # 组件目录
│ │── config # 配置目录
Expand Down Expand Up @@ -174,7 +173,7 @@ yarn vue-cli-service help # [命令] : 比如 yarn vue-cli-service help test:e2e
```

3. 输出目录为 `dist`, 包含 js/css/img/font/media 等文件夹
4. 所有 `config` 目录下的内容都会被打包到同一个文件`conf.*.js`(需要保留的注释请使用: `/*! 注释内容 */`), 用于支持直接修改配置而不必重新打包代码
4. `config` 目录下的所有内容都会被内联到对应`html`(需要保留的注释请使用: `/*! 注释内容 */`), 用于支持直接修改配置而不必重新打包代码
5. 测试用例目录层级与文件名应尽量与源码对应

> **提示和建议**
Expand Down Expand Up @@ -255,7 +254,7 @@ yarn vue-cli-service help # [命令] : 比如 yarn vue-cli-service help test:e2e
- 不要使用 `$` 作为组件事件名, 该名字已被[异步组件刷新](src/utils/highOrder.ts)占用
- 路由请**全部**使用异步组件(`@utils/highOrder getAsync`), 以使路由及其**子(异步)组件**可以局部刷新
- CSS Modules class 名使用 `camelCase` (global 可以 kebab-case), 选择器嵌套**不应超过三层**
- <a id="全局scss"></a>**全局 sccs** _(包含<a href="#别名">各别名</a>下[.env](.env) `GLOBAL_SCSS`变量指定的文件)_ 中不要出现具体样式, 也不要有[`:export{}`](https://github.com/css-modules/icss#export)(应在 `export*.scss` 中使用); 为保证`ts/js`中引入时 scss 变量注入正确, 应在合适的 scss 文件中引入目标样式源码:
- <a id="全局scss"></a>**全局 sccs** _(包含<a href="#别名">各别名</a>下[.env](.env) `GLOBAL_SCSS`变量指定的文件)_ 中不要出现具体样式, 也不要有[`:export{}`](https://github.com/css-modules/icss#export)(应在 `export` 目录下或 `export*.scss` 中使用); 为保证`ts/js`中引入时 scss 变量注入正确(使用缓存会导致无法对相同文件多次注入变量,不用缓存显然不合理), 应在合适的 scss 文件中引入目标样式源码:
```scss
// el.scss
Expand Down Expand Up @@ -324,18 +323,18 @@ yarn vue-cli-service help # [命令] : 比如 yarn vue-cli-service help test:e2e
```
- 尽量使用项目代码模板, 现有模板有(VSCode 输入左侧字符, [其他 IDE](.vscode/vue.code-snippets)):
- `ts`: `TypeScript` & `CSS Module`, vue 单文件组件中使用
- `vue`: `TypeScript` & `CSS Module`, `tsx` 文件中使用
- `js`: `JavaScript` & `CSS Module`, vue 单文件组件中使用
- `vue`: `JavaScript` & `CSS Module`, `jsx` 文件中使用
- `ts`: `TypeScript`, `vue` 文件中使用
- `vue`: `TypeScript`, `tsx` 文件中使用
- `js`: `JavaScript`, `vue` 文件中使用
- `vue`: `JavaScript`, `jsx` 文件中使用
- 请[规范](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md)提交消息
### 其他
- 关于多主题方案, 本模板采用的是 `<link rel="alternate stylesheet">`[方案](https://developer.mozilla.org/en-US/docs/Web/CSS/Alternative_style_sheets), 基于`scss`全局变量注入进行多个主题的构建, 优点是支持异步, 浏览器原生支持并且无缝流畅切换. 通过环境变量[.env](.env)进行配置, 在`import` **scss** 文件时可以指定主题和使用的 scss 变量(指定后不生成其他主题的)
```html
<script>
<script lang="ts">
/// 基础样式 ///
import './scss/a.scss?theme='
// 指定scss变量文件相对路径(别名、别名/主题文件夹)
Expand Down Expand Up @@ -574,16 +573,15 @@ yarn vue-cli-service help # [命令] : 比如 yarn vue-cli-service help test:e2e
- url 重写兼容旧版
- 反向代理, 绕过同源策略限制(api/图片等资源跨域等)
- 添加请求头字段 `access_token` 使后台能读到该字段(nginx 的 http 或 server 节点下需要添加配置`underscores_in_headers on; # 允许带下划线的请求头`)
- 开启 `gzip` 压缩, 并重用已有 gz 文件 `gzip_static on;`
- 缓存静态资源(html 可减小缓存时间)
- 开启 `gzip` 压缩, 并重用已有 `gz` 文件 `gzip_static on;`
- 缓存静态资源(html 可减少缓存时间)
配置示例(`{value}` 换成对应值):
配置示例(`xxx` 换成对应值):
```bash
http {
#underscores_in_headers on; # 允许带下划线的请求头
# 开启gZip
# 开启gZip(图片除外)
gzip on;
gzip_vary on;
gzip_static on;
Expand Down Expand Up @@ -634,7 +632,7 @@ http {

index index.html;
alias xxx/;
try_files $uri $uri/ /;
try_files $uri $uri.html $uri/ /;
}
location /api {
proxy_pass https?://xxx:xxx/xxx;
Expand Down
19 changes: 15 additions & 4 deletions build/devServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,21 @@ module.exports = function(ENV) {
if (!host || host === HOST) {
try {
const network = require('os').networkInterfaces()
host = network[Object.keys(network)[0]][1].address
} catch (e) {
host = 'localhost'
}
const family = ENV.DEV_SERVER_NETWORK || 'IPv4'
host = ''
for (const key in network) {
for (const item of network[key]) {
if (!item.internal && family === item.family) {
host = item.address
break
}
}
if (host) {
break
}
}
} catch (e) {}
host || (host = 'localhost')
}
let proxy

Expand Down
205 changes: 110 additions & 95 deletions build/insertPreload.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,62 @@
*/
const sourceMappingURL = require('source-map-url')

const PLUGIN_NAME = 'insert-preload'
const PLUGIN1 = 'insert-preload'
const PLUGIN2 = 'inline-manifest'

function getAssetName(chunks, reg) {
const match = []
for (let chunk of chunks) {
for (const chunk of chunks) {
reg.test(chunk.name) &&
match.push({ name: chunk.name, file: chunk.files[0] })
}
return match
}
function inlineWhenMatched(compilation, scripts, manifestAssetNames) {
return scripts.map(script => {
function inlineWhenMatched(compilation, scripts, manifestAssetNames, MERGE) {
const result = []
let inline
let script
let item
let src
for (script of scripts) {
if (script.tagName === 'script') {
const src = script.attributes.src
for (let item of manifestAssetNames) {
src = script.attributes.src
for (item of manifestAssetNames) {
if (src.indexOf(item.file) >= 0) {
return {
tagName: 'script',
closeTag: true,
attributes: {
type: 'text/javascript',
},
innerHTML: sourceMappingURL.removeFrom(
compilation.assets[item.file].source(),
),
src = sourceMappingURL.removeFrom(
compilation.assets[item.file].source(),
)
if (MERGE) {
if (inline) {
inline.innerHTML += src
} else {
inline = {
tagName: 'script',
closeTag: true,
attributes: {
type: 'text/javascript',
},
innerHTML: src,
}
}
} else {
delete script.attributes.src
script.innerHTML = src
result.push(script)
}
item = 0
break
}
}
if (!item) {
continue
}
}
result.push(script)
}
inline && result.push(inline)

return script
})
return result
}

/** 插入 preload 的资源插件
Expand All @@ -55,18 +79,19 @@ module.exports = class {
* }
*/
constructor(option = {}) {
/// 内联runtime ///
let runtime = option.runtime
if (runtime) {
if (typeof runtime.test === 'function') {
this._REG_RUNTIME = runtime
} else {
typeof runtime === 'string' && (runtime = [runtime])
for (let i = 0; i < runtime.length; i++) {
runtime[i] = new RegExp(runtime[i])
runtime[i] = new RegExp('(?<![^\\\\/])' + runtime[i])
}
this._REG_RUNTIME = {
test(str) {
for (let reg of runtime) {
for (const reg of runtime) {
if (reg.test(str)) {
return true
}
Expand All @@ -76,105 +101,95 @@ module.exports = class {
}
}
}
/// 脚本属性 ///
this._SA =
option.defer === false ? option.async === true && 'async' : 'defer'
}

// https://webpack.docschina.org/api/plugins/
apply(compiler) {
// 添加 preload 的资源
compiler.hooks.compilation.tap(
PLUGIN_NAME,
compilation =>
// html-webpack-plugin 钩子
compilation.hooks.htmlWebpackPluginAlterAssetTags &&
/// insert-preload ///
compiler.hooks.compilation.tap(PLUGIN1, compilation => {
// html-webpack-plugin 钩子
compilation.hooks.htmlWebpackPluginAlterAssetTags &&
compilation.hooks.htmlWebpackPluginAlterAssetTags.tap(
PLUGIN_NAME,
PLUGIN1,
htmlPluginData => this.insert(htmlPluginData),
),
)
// inline-manifest
)
})
/// inline-manifest ///
const REG_RUNTIME = this._REG_RUNTIME
if (REG_RUNTIME) {
compiler.hooks.emit.tap(PLUGIN_NAME, compilation => {
for (let item of getAssetName(compilation.chunks, REG_RUNTIME)) {
const MERGE = this._SA
compiler.hooks.emit.tap(PLUGIN2, compilation => {
for (const item of getAssetName(compilation.chunks, REG_RUNTIME)) {
delete compilation.assets[item.file]
}
})
compiler.hooks.compilation.tap(PLUGIN_NAME, compilation => {
compilation.hooks.htmlWebpackPluginAlterAssetTags.tapAsync(
PLUGIN_NAME,
(data, cb) => {
const manifestAssetNames = getAssetName(
compilation.chunks,
REG_RUNTIME,
)
compiler.hooks.compilation.tap(PLUGIN2, compilation => {
compilation.hooks.htmlWebpackPluginAlterAssetTags &&
compilation.hooks.htmlWebpackPluginAlterAssetTags.tapAsync(
PLUGIN2,
(data, cb) => {
const manifestAssetNames = getAssetName(
compilation.chunks,
REG_RUNTIME,
)

manifestAssetNames.length &&
['head', 'body'].forEach(section => {
data[section] = inlineWhenMatched(
compilation,
data[section],
manifestAssetNames,
)
})
manifestAssetNames.length &&
['head', 'body'].forEach(section => {
data[section] = inlineWhenMatched(
compilation,
data[section],
manifestAssetNames,
MERGE,
)
})

cb(null, data)
},
)
cb(null, data)
},
)

compilation.hooks.htmlWebpackPluginBeforeHtmlGeneration.tapAsync(
PLUGIN_NAME,
(htmlPluginData, cb) => {
const runtime = []
const assets = htmlPluginData.assets
const manifestAssetNames = getAssetName(
compilation.chunks,
REG_RUNTIME,
)
compilation.hooks.htmlWebpackPluginBeforeHtmlGeneration &&
compilation.hooks.htmlWebpackPluginBeforeHtmlGeneration.tapAsync(
PLUGIN2,
(htmlPluginData, cb) => {
const runtime = []
const assets = htmlPluginData.assets
const manifestAssetNames = getAssetName(
compilation.chunks,
REG_RUNTIME,
)

if (
manifestAssetNames.length &&
htmlPluginData.plugin.options.inject === false
) {
for (let item of manifestAssetNames) {
runtime.push('<script>')
runtime.push(
sourceMappingURL.removeFrom(
compilation.assets[item.file].source(),
),
)
runtime.push('</script>')
if (
manifestAssetNames.length &&
htmlPluginData.plugin.options.inject === false
) {
for (const item of manifestAssetNames) {
runtime.push('<script>')
runtime.push(
sourceMappingURL.removeFrom(
compilation.assets[item.file].source(),
),
)
runtime.push('</script>')

const runtimeIndex = assets.js.indexOf(
assets.publicPath + item.file,
)
if (runtimeIndex >= 0) {
assets.js.splice(runtimeIndex, 1)
delete assets.chunks[item.name]
const runtimeIndex = assets.js.indexOf(
assets.publicPath + item.file,
)
if (runtimeIndex >= 0) {
assets.js.splice(runtimeIndex, 1)
delete assets.chunks[item.name]
}
}
}
}

assets.runtime = runtime.join('')
cb(null, htmlPluginData)
},
)
assets.runtime = runtime.join('')
cb(null, htmlPluginData)
},
)
})
}
// 不能这么加loader
// const REG_EXCLUDE = /[\\/]node_modules[\\/]/
// const REG_INCLUDE = /\.(?:ts|vue|tsx|js|jsx)$/
// compiler.hooks.thisCompilation.tap(PLUGIN_NAME, compilation =>
// compilation.hooks.buildModule.tap(
// PLUGIN_NAME,
// module =>
// THEMES &&
// !REG_EXCLUDE.test(module.rawRequest) &&
// REG_INCLUDE.test(module.rawRequest) &&
// module.loaders.push(themeLoader)
// )
// )
}

// 补充缺失的资源
Expand Down
Loading

0 comments on commit 9901cf1

Please sign in to comment.