提交 6de497bc 编写于 作者: fxy060608's avatar fxy060608

feat(cli): add mp template cache

上级 e4962592
......@@ -109,6 +109,17 @@ at ${resourcePath}.vue:1`)
* ...暂时使用方案1
*/
if (options.emitFile) {
// cache
if (process.env.UNI_USING_CACHE) {
const oldEmitFile = options.emitFile
process.UNI_CACHE_TEMPLATES = {}
options.emitFile = function emitFile (name, content) {
const absolutePath = path.resolve(process.env.UNI_OUTPUT_DIR, name)
process.UNI_CACHE_TEMPLATES[absolutePath] = content
oldEmitFile(name, content)
}
}
if (options.updateSpecialMethods) {
options.updateSpecialMethods(resourcePath, [...res.specialMethods])
}
......
......@@ -6,7 +6,7 @@ const {
isInHBuilderX
} = require('@dcloudio/uni-cli-shared')
// override
// nvue override
moduleAlias.addAlias('weex-styler', path.resolve(__dirname, 'packages/weex-styler'))
moduleAlias.addAlias('weex-template-compiler', path.resolve(__dirname, 'packages/weex-template-compiler'))
moduleAlias.addAlias('./compileTemplate', path.resolve(__dirname,
......@@ -19,6 +19,14 @@ moduleAlias.addAlias('./templateLoader', (fromPath, request, alias) => {
}
return request
})
// vue cache
moduleAlias.addAlias('./loaders/pitcher', (fromPath, request, alias) => {
if (fromPath.indexOf('vue-loader') !== -1) {
return path.resolve(__dirname, 'packages/vue-loader/lib/loaders/pitcher')
}
return request
})
if (isInHBuilderX) {
moduleAlias.addAlias('typescript', path.resolve(process.env.UNI_HBUILDERX_PLUGINS,
'compile-typescript/node_modules/typescript'))
......
const qs = require('querystring')
const loaderUtils = require('loader-utils')
const hash = require('hash-sum')
const selfPath = require.resolve('vue-loader/lib/index')
const templateLoaderPath = require.resolve('vue-loader/lib/loaders/templateLoader')
const stylePostLoaderPath = require.resolve('vue-loader/lib/loaders/stylePostLoader')
const isESLintLoader = l => /(\/|\\|@)eslint-loader/.test(l.path)
const isNullLoader = l => /(\/|\\|@)null-loader/.test(l.path)
const isCSSLoader = l => /(\/|\\|@)css-loader/.test(l.path)
const isCacheLoader = l => /(\/|\\|@)cache-loader/.test(l.path)
const isPitcher = l => l.path !== __filename
const isPreLoader = l => !l.pitchExecuted
const isPostLoader = l => l.pitchExecuted
const dedupeESLintLoader = loaders => {
const res = []
let seen = false
loaders.forEach(l => {
if (!isESLintLoader(l)) {
res.push(l)
} else if (!seen) {
seen = true
res.push(l)
}
})
return res
}
const shouldIgnoreCustomBlock = loaders => {
const actualLoaders = loaders.filter(loader => {
// vue-loader
if (loader.path === selfPath) {
return false
}
// cache-loader
if (isCacheLoader(loader)) {
return false
}
return true
})
return actualLoaders.length === 0
}
module.exports = code => code
// This pitching loader is responsible for intercepting all vue block requests
// and transform it into appropriate requests.
module.exports.pitch = function (remainingRequest) {
const options = loaderUtils.getOptions(this)
const { cacheDirectory, cacheIdentifier } = options
const query = qs.parse(this.resourceQuery.slice(1))
let loaders = this.loaders
// if this is a language block request, eslint-loader may get matched
// multiple times
if (query.type) {
// if this is an inline block, since the whole file itself is being linted,
// remove eslint-loader to avoid duplicate linting.
if (/\.vue$/.test(this.resourcePath)) {
loaders = loaders.filter(l => !isESLintLoader(l))
} else {
// This is a src import. Just make sure there's not more than 1 instance
// of eslint present.
loaders = dedupeESLintLoader(loaders)
}
}
// remove self
loaders = loaders.filter(isPitcher)
// do not inject if user uses null-loader to void the type (#1239)
if (loaders.some(isNullLoader)) {
return
}
const genRequest = loaders => {
// Important: dedupe since both the original rule
// and the cloned rule would match a source import request.
// also make sure to dedupe based on loader path.
// assumes you'd probably never want to apply the same loader on the same
// file twice.
// Exception: in Vue CLI we do need two instances of postcss-loader
// for user config and inline minification. So we need to dedupe baesd on
// path AND query to be safe.
const seen = new Map()
const loaderStrings = []
loaders.forEach(loader => {
const identifier = typeof loader === 'string'
? loader
: (loader.path + loader.query)
const request = typeof loader === 'string' ? loader : loader.request
if (!seen.has(identifier)) {
seen.set(identifier, true)
// loader.request contains both the resolved loader path and its options
// query (e.g. ??ref-0)
loaderStrings.push(request)
}
})
return loaderUtils.stringifyRequest(this, '-!' + [
...loaderStrings,
this.resourcePath + this.resourceQuery
].join('!'))
}
// Inject style-post-loader before css-loader for scoped CSS and trimming
if (query.type === `style`) {
const cssLoaderIndex = loaders.findIndex(isCSSLoader)
if (cssLoaderIndex > -1) {
const afterLoaders = loaders.slice(0, cssLoaderIndex + 1)
const beforeLoaders = loaders.slice(cssLoaderIndex + 1)
const request = genRequest([
...afterLoaders,
stylePostLoaderPath,
...beforeLoaders
])
// console.log(request)
return `import mod from ${request}; export default mod; export * from ${request}`
}
}
// for templates: inject the template compiler & optional cache
if (query.type === `template`) {
const path = require('path')
// fixed by xxxxxx
const cacheLoader = cacheDirectory && cacheIdentifier
? [`${require.resolve('cache-loader')}??uni-cache-loader-template-options`]
: []
const preLoaders = loaders.filter(isPreLoader)
const postLoaders = loaders.filter(isPostLoader)
const request = genRequest([
...cacheLoader,
...postLoaders,
templateLoaderPath + `??vue-loader-options`,
...preLoaders
])
// console.log(request)
// the template compiler uses esm exports
return `export * from ${request}`
}
// if a custom block has no other matching loader other than vue-loader itself
// or cache-loader, we should ignore it
if (query.type === `custom` && shouldIgnoreCustomBlock(loaders)) {
return ``
}
// When the user defines a rule that has only resourceQuery but no test,
// both that rule and the cloned rule will match, resulting in duplicated
// loaders. Therefore it is necessary to perform a dedupe here.
const request = genRequest(loaders)
return `import mod from ${request}; export default mod; export * from ${request}`
}
......@@ -2,8 +2,8 @@ const loaderUtils = require('loader-utils')
module.exports = function(content) {
this.cacheable && this.cacheable()
const vueLoaderOptions = this.loaders.find(loader => loader.ident)
if (vueLoaderOptions && vueLoaderOptions && vueLoaderOptions.ident === 'vue-loader-options') {
const vueLoaderOptions = this.loaders.find(loader => loader.ident === 'vue-loader-options')
if (vueLoaderOptions) {
const params = loaderUtils.parseQuery(this.resourceQuery)
if (params.recyclable) {
Object.assign(vueLoaderOptions.options.compilerOptions, {
......
......@@ -25,8 +25,8 @@ module.exports = (api, options) => {
require('./lib/options')(options)
api.configureWebpack(require('./lib/configure-webpack')(platformOptions, manifestPlatformOptions, options))
api.chainWebpack(require('./lib/chain-webpack')(platformOptions))
api.configureWebpack(require('./lib/configure-webpack')(platformOptions, manifestPlatformOptions, options, api))
api.chainWebpack(require('./lib/chain-webpack')(platformOptions, api))
}
module.exports.defaultModes = {
......
const fs = require('fs')
const path = require('path')
const mkdirp = require('mkdirp')
const BJSON = require('buffer-json')
const {
getPartialIdentifier
} = require('./util')
const directories = new Set()
function write (key, data, callback) {
const dirname = path.dirname(key)
// template,缓存 mp template
if (
data.remainingRequest.indexOf('vue&type=template') !== -1 &&
process.UNI_CACHE_TEMPLATES
) {
data['mpTemplates'] = process.UNI_CACHE_TEMPLATES
delete process.UNI_CACHE_TEMPLATES
}
const content = BJSON.stringify(data)
if (directories.has(dirname)) {
// for performance skip creating directory
fs.writeFile(key, content, 'utf-8', callback)
} else {
mkdirp(dirname, (mkdirErr) => {
if (mkdirErr) {
callback(mkdirErr)
return
}
directories.add(dirname)
fs.writeFile(key, content, 'utf-8', callback)
})
}
}
function read (key, callback) {
fs.readFile(key, 'utf-8', (err, content) => {
if (err) {
callback(err)
return
}
try {
const data = BJSON.parse(content)
const mpTemplates = data['mpTemplates']
if (mpTemplates) {
Object.keys(mpTemplates).forEach(name => {
console.log('read=>write', name)
fs.writeFileSync(name, mpTemplates[name], 'utf-8')
})
}
callback(null, data)
} catch (e) {
callback(e)
}
})
}
module.exports = {
createTemplateCacheLoader: function (api) {
return {
resourceQuery: /type=uni-cache-loader-template/,
use: [{
loader: 'cache-loader',
ident: 'uni-cache-loader-template-options',
options: Object.assign(api.genCacheConfig(
'uni-template-compiler/' + process.env.UNI_PLATFORM,
getPartialIdentifier()
), {
read,
write
})
}]
}
}
}
......@@ -4,11 +4,15 @@ const {
sassLoaderVersion
} = require('@dcloudio/uni-cli-shared/lib/scss')
const {
getPartialIdentifier
} = require('./util')
function resolve (dir) {
return path.resolve(__dirname, '..', dir)
}
module.exports = function chainWebpack (platformOptions) {
module.exports = function chainWebpack (platformOptions, api) {
const {
runByHBuilderX, // 使用 HBuilderX 运行
cssPreprocessOptions
......@@ -48,11 +52,22 @@ module.exports = function chainWebpack (platformOptions) {
const langRule = webpackConfig.module.rule(lang)
const loader = loaders[lang]
cssTypes.forEach(type => {
if (process.env.UNI_USING_CACHE) {
langRule.oneOf(type)
.use(`uniapp-cache-css`)
.loader('cache-loader')
.options(api.genCacheConfig(
'css-loader/' + process.env.UNI_PLATFORM,
getPartialIdentifier()
))
.before('css-loader')
}
langRule.oneOf(type)
.use(`uniapp-preprocss`)
.loader(resolve('packages/webpack-preprocess-loader'))
.options(cssPreprocessOptions)
.before('css-loader') // 在 css-loader 之后条件编译一次,避免 import 进来的 css 没有走条件编译
if (loader) { // 在 scss,less,stylus 之前先条件编译一次
langRule.oneOf(type)
.use(`uniapp-preprocss-` + lang)
......@@ -79,7 +94,7 @@ module.exports = function chainWebpack (platformOptions) {
})
}
platformOptions.chainWebpack(webpackConfig)
platformOptions.chainWebpack(webpackConfig, api)
// define
webpackConfig
.plugin('uni-define')
......
......@@ -6,6 +6,10 @@ const CopyWebpackPlugin = require('copy-webpack-plugin')
const merge = require('webpack-merge')
const {
getPartialIdentifier
} = require('./util')
function resolve (dir) {
return path.resolve(__dirname, '..', dir)
}
......@@ -14,7 +18,7 @@ function resolveModule (dir) {
return path.resolve(__dirname, '../../..', dir)
}
module.exports = function configureWebpack (platformOptions, manifestPlatformOptions, vueOptions) {
module.exports = function configureWebpack (platformOptions, manifestPlatformOptions, vueOptions, api) {
const {
runByHBuilderX, // 使用 HBuilderX 运行
isInHBuilderX, // 在 HBuilderX 的插件中
......@@ -138,13 +142,19 @@ module.exports = function configureWebpack (platformOptions, manifestPlatformOpt
return function (webpackConfig) {
// disable js cache-loader
const rawRules = webpackConfig.module.rules
for (let i = rawRules.length - 1; i >= 0; i--) {
const uses = rawRules[i].use
if (Array.isArray(uses)) {
if (uses.find(use => use.loader === 'babel-loader')) {
const index = uses.findIndex(use => use.loader === 'cache-loader')
uses.splice(index, 1)
if (process.env.UNI_USING_CACHE) {
Object.assign(uses[index].options, api.genCacheConfig(
'babel-loader/' + process.env.UNI_PLATFORM,
getPartialIdentifier()
))
} else {
uses.splice(index, 1)
}
}
}
}
......@@ -168,7 +178,7 @@ module.exports = function configureWebpack (platformOptions, manifestPlatformOpt
let platformWebpackConfig = platformOptions.webpackConfig
if (typeof platformWebpackConfig === 'function') {
platformWebpackConfig = platformWebpackConfig(webpackConfig)
platformWebpackConfig = platformWebpackConfig(webpackConfig, api)
}
// 移除 node_modules 目录,避免受路径上的 node_modules 影响
webpackConfig.resolve.modules = webpackConfig.resolve.modules.filter(module => module !==
......
......@@ -26,6 +26,10 @@ process.env.UNI_CLI_CONTEXT = path.resolve(__dirname, '../../../../')
process.UNI_LIBRARIES = process.UNI_LIBRARIES || ['@dcloudio/uni-ui']
if (process.env.NODE_ENV === 'production') { // 发行模式,不启用 cache
process.env.UNI_USING_CACHE = false
}
const {
isSupportSubPackages,
runByHBuilderX,
......@@ -254,8 +258,8 @@ if (runByHBuilderX) {
}
}
console.log(`正在编译中...`)
runByHBuilderX && console.log(`正在编译中...`)
module.exports = {
manifestPlatformOptions: platformOptions
}
}
......@@ -4,10 +4,11 @@ const path = require('path')
const {
getMainEntry,
getH5Options,
getPlatformCompiler,
getPlatformCssnano
} = require('@dcloudio/uni-cli-shared')
const modifyVueLoader = require('./vue-loader')
const WebpackHtmlAppendPlugin = require('../../packages/webpack-html-append-plugin')
function resolve (dir) {
......@@ -100,7 +101,7 @@ module.exports = {
plugins
}
},
chainWebpack (webpackConfig) {
chainWebpack (webpackConfig, api) {
webpackConfig.plugins.delete('copy')
if (!process.env.UNI_OPT_PREFETCH) {
......@@ -109,29 +110,8 @@ module.exports = {
if (!process.env.UNI_OPT_PRELOAD) {
webpackConfig.plugins.delete('preload-index')
}
// Vue
webpackConfig.module
.rule('vue')
.test([/\.vue$/, /\.nvue$/])
.use('vue-loader')
.tap(options => Object.assign(options, {
compiler: getPlatformCompiler(),
compilerOptions: require('./compiler-options'),
cacheDirectory: false,
cacheIdentifier: false
}))
.end()
.use('uniapp-custom-block-loader')
.loader(require.resolve('@dcloudio/vue-cli-plugin-uni/packages/webpack-custom-block-loader'))
.options({
compiler: getPlatformCompiler()
})
.end()
.use('uniapp-scoped')
.loader(resolve('packages/webpack-scoped-loader'))
.end()
.uses
.delete('cache-loader')
modifyVueLoader(webpackConfig, require('./compiler-options'), api)
if (process.env.NODE_ENV === 'production') {
const module = webpackConfig.module
......
......@@ -9,13 +9,14 @@ const {
parseEntry,
getMainEntry,
getPlatformExts,
getPlatformCompiler,
getPlatformCssnano
} = require('@dcloudio/uni-cli-shared')
const modifyVueLoader = require('./vue-loader')
const {
isUnaryTag
} = require('./util')
createTemplateCacheLoader
} = require('./cache-loader')
function createUniMPPlugin () {
if (process.env.UNI_USING_COMPONENTS) {
......@@ -42,7 +43,7 @@ function getProvides () {
process.env.UNI_PLATFORM === 'app-plus' &&
process.env.UNI_USING_V8
) {
provides['__f__'] = [path.resolve(__dirname, 'format-log.js'), 'default']
provides['__f__'] = [path.resolve(__dirname, 'format-log.js'), 'default']
provides['crypto'] = [path.resolve(__dirname, 'crypto.js'), 'default']
}
......@@ -61,7 +62,7 @@ module.exports = {
vueConfig: {
parallel: false
},
webpackConfig (webpackConfig) {
webpackConfig (webpackConfig, api) {
if (!webpackConfig.optimization) {
webpackConfig.optimization = {}
}
......@@ -105,9 +106,9 @@ module.exports = {
chunkFilename: '[id].js',
globalObject: process.env.UNI_PLATFORM === 'mp-alipay' ? 'my' : 'global',
sourceMapFilename: '../.sourcemap/' + process.env.UNI_PLATFORM + '/[name].js.map'
},
performance: {
hints: false
},
performance: {
hints: false
},
resolve: {
extensions: ['.nvue'],
......@@ -132,7 +133,7 @@ module.exports = {
use: [{
loader: '@dcloudio/webpack-uni-mp-loader/lib/template'
}]
}, {
}, createTemplateCacheLoader(api), {
resourceQuery: [
/lang=wxs/,
/lang=filter/,
......@@ -153,7 +154,7 @@ module.exports = {
]
}
},
chainWebpack (webpackConfig) {
chainWebpack (webpackConfig, api) {
if (process.env.UNI_PLATFORM === 'mp-baidu') {
webpackConfig.module
.rule('js')
......@@ -161,32 +162,9 @@ module.exports = {
.add(/\.filter\.js$/)
}
// disable vue cache-loader
webpackConfig.module
.rule('vue')
.test([/\.vue$/, /\.nvue$/])
.use('vue-loader')
.tap(options => Object.assign(options, {
compiler: getPlatformCompiler(),
compilerOptions: process.env.UNI_USING_COMPONENTS ? {
isUnaryTag,
preserveWhitespace: false
} : require('./mp-compiler-options'),
cacheDirectory: false,
cacheIdentifier: false
}))
.end()
.use('uniapp-custom-block-loader')
.loader(require.resolve('@dcloudio/vue-cli-plugin-uni/packages/webpack-custom-block-loader'))
.options({
compiler: getPlatformCompiler()
})
.end()
.use('uniapp-nvue-loader')
.loader(require.resolve('@dcloudio/webpack-uni-mp-loader/lib/style.js'))
.end()
.uses
.delete('cache-loader')
const compilerOptions = process.env.UNI_USING_COMPONENTS ? {} : require('./mp-compiler-options')
modifyVueLoader(webpackConfig, compilerOptions, api)
const styleExt = getPlatformExts().style
......
......@@ -9,9 +9,24 @@ function makeMap (str, expectsLowerCase) {
: val => map[val]
}
let partialIdentifier = false
module.exports = {
isUnaryTag: makeMap(
'image,area,base,br,col,embed,frame,hr,img,input,isindex,keygen,' +
'link,meta,param,source,track,wbr'
)
),
getPartialIdentifier () {
if (partialIdentifier) {
return partialIdentifier
}
partialIdentifier = {
'UNI_COMPILER_VERSION': require('../package.json').version
}
Object.keys(process.env).forEach(name => {
if (name.indexOf('UNI_') === 0) {
partialIdentifier[name] = process.env[name]
}
})
return partialIdentifier
}
}
const {
getPlatformCompiler
} = require('@dcloudio/uni-cli-shared')
const {
isUnaryTag,
getPartialIdentifier
} = require('./util')
module.exports = function modifyVueLoader (webpackConfig, compilerOptions, api) {
// vue-loader options
const cacheConfig = {
cacheDirectory: false,
cacheIdentifier: false
}
const partialIdentifier = {}
if (process.env.UNI_USING_CACHE) {
Object.assign(cacheConfig, api.genCacheConfig(
'vue-template-compiler/' + process.env.UNI_PLATFORM,
getPartialIdentifier()
))
}
webpackConfig.module
.rule('vue')
.test([/\.vue$/, /\.nvue$/])
.use('vue-loader')
.tap(options => Object.assign(options, {
compiler: getPlatformCompiler(),
compilerOptions: Object.assign({
isUnaryTag,
preserveWhitespace: false
}, compilerOptions)
}, cacheConfig))
.end()
.use('uniapp-custom-block-loader')
.loader(require.resolve('@dcloudio/vue-cli-plugin-uni/packages/webpack-custom-block-loader'))
.options({
compiler: getPlatformCompiler()
})
// h5 框架需要使用 scoped 样式,其他平台编译时识别是否 nvue 文件且注入 flex 相关样式
if (process.env.UNI_PLATFORM === 'h5') {
webpackConfig.module
.rule('vue')
.use('uniapp-h5-style-scoped')
.loader(require.resolve('@dcloudio/vue-cli-plugin-uni/packages/webpack-scoped-loader'))
} else {
webpackConfig.module
.rule('vue')
.use('uniapp-nvue-style-loader')
.loader(require.resolve('@dcloudio/webpack-uni-mp-loader/lib/style.js'))
}
// 是否启用 cache
if (process.env.UNI_USING_CACHE) {
webpackConfig.module
.rule('vue')
.use('cache-loader')
.tap(options => Object.assign(options, api.genCacheConfig(
'vue-loader/' + process.env.UNI_PLATFORM,
partialIdentifier
)))
} else {
webpackConfig.module
.rule('vue')
.uses
.delete('cache-loader')
}
}
......@@ -18,11 +18,13 @@
"license": "Apache-2.0",
"dependencies": {
"@dcloudio/uni-stat": "^2.0.0-23320190923002",
"buffer-json": "^2.0.0",
"copy-webpack-plugin": "^4.6.0",
"cross-env": "^5.2.0",
"envinfo": "^6.0.1",
"hash-sum": "^1.0.2",
"loader-utils": "^1.1.0",
"mkdirp": "^0.5.1",
"module-alias": "^2.1.0",
"postcss": "^7.0.7",
"postcss-import": "^12.0.1",
......
......@@ -5,8 +5,8 @@ const loaderUtils = require('loader-utils')
module.exports = function(content) {
this.cacheable && this.cacheable()
const vueLoaderOptions = this.loaders.find(loader => loader.ident)
if (vueLoaderOptions && vueLoaderOptions.ident === 'vue-loader-options') {
const vueLoaderOptions = this.loaders.find(loader => loader.ident === 'vue-loader-options')
if (vueLoaderOptions) {
const params = loaderUtils.parseQuery(this.resourceQuery)
/* eslint-disable no-mixed-operators */
const filterModules = JSON.parse(params && params['filter-modules'] || '{}')
......
......@@ -32,8 +32,8 @@ const filterTagName = getPlatformFilterTag() || ''
module.exports = function (content) {
this.cacheable && this.cacheable()
const vueLoaderOptions = this.loaders.find(loader => loader.ident)
if (vueLoaderOptions && vueLoaderOptions.ident === 'vue-loader-options') {
const vueLoaderOptions = this.loaders.find(loader => loader.ident === 'vue-loader-options')
if (vueLoaderOptions) {
const globalUsingComponents = getGlobalUsingComponents()
const realResourcePath = path.relative(process.env.UNI_INPUT_DIR, this.resourcePath)
const resourcePath = normalizeNodeModules(removeExt(realResourcePath) + templateExt)
......
......@@ -25,8 +25,8 @@ module.exports = function (content) {
if (process.env.UNI_USING_COMPONENTS) {
// 向 uni-template-compier 传递 emitFile
const vueLoaderOptions = this.loaders.find(loader => loader.ident)
if (vueLoaderOptions && vueLoaderOptions.ident === 'vue-loader-options') {
const vueLoaderOptions = this.loaders.find(loader => loader.ident === 'vue-loader-options')
if (vueLoaderOptions) {
Object.assign(vueLoaderOptions.options.compilerOptions, {
resourcePath: removeExt(realResourcePath) + templateExt,
emitFile: this.emitFile
......@@ -57,8 +57,8 @@ module.exports = function (content) {
cacheCompilerOptions(realResourcePath, compilerOptions)
// 向 vue-loader templateLoader 传递 compilerOptions
const vueLoaderOptions = this.loaders.find(loader => loader.ident)
if (vueLoaderOptions && vueLoaderOptions.ident === 'vue-loader-options') {
const vueLoaderOptions = this.loaders.find(loader => loader.ident === 'vue-loader-options')
if (vueLoaderOptions) {
Object.assign(vueLoaderOptions.options.compilerOptions, compilerOptions)
} else {
throw new Error('vue-loader-options parse error')
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册