Skip to content

Commit

Permalink
Merge pull request #44 from Topthinking/master
Browse files Browse the repository at this point in the history
优化样式解析策略
  • Loading branch information
Topthinking committed Apr 17, 2020
2 parents 9f404d9 + 965e45a commit 6fdaaab
Show file tree
Hide file tree
Showing 15 changed files with 116 additions and 138 deletions.
4 changes: 2 additions & 2 deletions lerna.json
@@ -1,7 +1,7 @@
{
"version": "0.15.1",
"version": "0.16.0-alpha.3",
"versions": {
"alpha": "0.15.0-alpha.1"
"alpha": "0.16.0-alpha.3"
},
"hoist": true,
"npmClient": "yarn",
Expand Down
17 changes: 12 additions & 5 deletions packages/award-fetch/src/utils/transformRequest.ts
Expand Up @@ -6,13 +6,20 @@ import isUndefined = require('lodash/isUndefined');

/**
* a=1 => {a: 1}
* "[{a:2}]" => [{a:2}]
*/
function string2Object(str: string) {
return str.split('&').reduce((res: object, item: string) => {
const arr = item.split('=');
(res as any)[arr[0]] = arr[1];
return res;
}, {});
let data = {};
try {
data = JSON.parse(str);
} catch (error) {
data = str.split('&').reduce((res: object, item: string) => {
const arr = item.split('=');
(res as any)[arr[0]] = arr[1];
return res;
}, {});
}
return data;
}

/**
Expand Down
10 changes: 4 additions & 6 deletions packages/award-scripts/src/scripts/build.ts
@@ -1,10 +1,9 @@
import * as chalk from 'chalk';
import * as path from 'path';
import * as fs from 'fs-extra';
import clean from '../tools/tool/clean';
import web_ssr from '../library/build/web_ssr';
import web_server from '../library/build/web_server';
import { clearConsole } from '../tools/tool';
import { clearConsole, constant } from '../tools/tool';

export default {
command: 'build',
Expand Down Expand Up @@ -33,11 +32,10 @@ export default {
} else if (argv.node) {
await web_server();
} else {
const cache = path.join(process.cwd(), 'node_modules', '.cache', 'award');
if (fs.existsSync(cache)) {
clean(cache);
if (fs.existsSync(constant.CACHE_DIR)) {
clean(constant.CACHE_DIR);
}
fs.mkdirpSync(cache);
fs.mkdirpSync(constant.CACHE_DIR);
await web_ssr();
await web_server();
}
Expand Down
9 changes: 4 additions & 5 deletions packages/award-scripts/src/scripts/export.ts
@@ -1,6 +1,6 @@
import * as path from 'path';
import * as fs from 'fs-extra';
import clean from '../tools/tool/clean';
import constant from '../tools/tool/constant';
import exportProject from '../library/export';

export default {
Expand All @@ -23,11 +23,10 @@ export default {
async action(argv: any) {
process.env.WEB_TYPE = 'WEB_SPA';
process.env.EXPORTRUNHTML = argv.html ? '1' : '0';
const cache = path.join(process.cwd(), 'node_modules', '.cache', 'award');
if (fs.existsSync(cache)) {
clean(cache);
if (fs.existsSync(constant.CACHE_DIR)) {
clean(constant.CACHE_DIR);
}
fs.mkdirpSync(cache);
fs.mkdirpSync(constant.CACHE_DIR);
exportProject({
browser: argv.browser,
local: argv.local,
Expand Down
@@ -1,11 +1,12 @@
import * as fs from 'fs';
import hashString = require('string-hash');
import * as path from 'path';
import constant from '../../../../tool/constant';

const pluginName = 'WriteStyleMapPlugin';
const cwd = process.cwd();

const styleModulefile = path.join(cwd, 'node_modules/.cache/award/.moduleStyles.json');
const styleModulefile = path.join(constant.CACHE_DIR, '.moduleStyles.json');

const originFile = (pwd: string, origin: string) => {
const file = path.join(pwd, origin);
Expand Down
Expand Up @@ -8,6 +8,7 @@ import FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
import { HmrTimePlugin, ProgressBarPlugin, ReactLoadablePlugin } from '../../webpack-plugins';
import webpackInclude from '../utils/include';
import alias from '../utils/alias';
import toolConstant from '../../tool/constant';

const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const ForkTsCheckerNotifierWebpackPlugin = require('fork-ts-checker-notifier-webpack-plugin');
Expand Down Expand Up @@ -82,7 +83,7 @@ export default (entry: string, assetPrefixs: string): webpack.Configuration => {
}),
new FriendlyErrorsWebpackPlugin(),
new ReactLoadablePlugin({
filename: path.join(cwd, 'node_modules/.cache/award/' + constant['REACT-LOADABEL'])
filename: path.join(toolConstant.CACHE_DIR, constant['REACT-LOADABEL'])
}),
new HmrTimePlugin(),
new webpack.HotModuleReplacementPlugin(),
Expand Down
10 changes: 2 additions & 8 deletions packages/award-scripts/src/tools/server/server.ts
Expand Up @@ -10,13 +10,12 @@ import * as chalk from 'chalk';
import { renderToString } from 'react-dom/server';
import { Helmet as Head } from 'react-helmet';
import { getBundles } from 'react-loadable/webpack';
import * as Loadable from 'react-loadable';
import { Server } from 'award-server';
import openBrowser = require('open-chrome-refresh');
import { serverInfo, getAwardConfig } from 'award-utils/server';
import { IServerEntry, IContext } from 'award-types';

import { extensions, clearConsole } from '../tool';
import { extensions, clearConsole, constant as toolConstant } from '../tool';
import { register } from '../babel';
import remove = require('../remove');
import { constant } from '../help';
Expand Down Expand Up @@ -166,15 +165,10 @@ export default class DevServer extends Server {
(this as any).renderReactToString = async (Component: any, ctx: IContext) => {
let stats = null;
// 加载bundle json
const filename = path.join(
this.dir,
'node_modules/.cache/award/' + constant['REACT-LOADABEL']
);
const filename = path.join(toolConstant.CACHE_DIR, constant['REACT-LOADABEL']);
if (fs.existsSync(filename)) {
stats = JSON.parse(fs.readFileSync(filename, 'utf-8'));
}
// 优先初始化import
await Loadable.preloadAll();

// 渲染组件
const html = renderToString(Component);
Expand Down
Expand Up @@ -3,6 +3,7 @@
*/
import { join } from 'path';
import { fromJS } from 'immutable';
import * as Loadable from 'react-loadable';
import { DocumentComponent } from 'award-utils/server';
import removeModule = require('../../remove');
import { IConfig, IServer } from 'award-types';
Expand All @@ -19,6 +20,8 @@ export default function(this: IServer) {

// 引用入口直接执行AppRegistry
require(this.RootPageEntry);
// 预加载所有模块
await Loadable.preloadAll();

if (this.RootComponent === null) {
// 说明没有执行start方法
Expand Down
39 changes: 13 additions & 26 deletions packages/award-scripts/src/tools/style/babel/handleStyles.ts
Expand Up @@ -4,7 +4,6 @@
* 寻找到当前js里面的第一个JSXElement组件,然后开始分析样式
*/
import hashString = require('string-hash');
import { loopWhile } from 'deasync';
import chalk = require('chalk');
import md5 = require('md5');
import * as fs from 'fs-extra';
Expand All @@ -15,12 +14,12 @@ import postcssParse from '../postcss-parse';
import { dev } from '../utils';
import { getHashByReference } from '../../tool/createProjectFileHash';
import clean from '../../tool/clean';
import constant from '../../tool/constant';

// 记录样式是否出现重复的引用,主要用来做css导出使用的
const styleIds: any = [];
const globalIds: any = [];
const cwd = process.cwd();
const styleCacheDir = path.join(cwd, 'node_modules/.cache/award');

// eslint-disable-next-line complexity
export default (cache: any, state: any) => {
Expand All @@ -43,18 +42,12 @@ export default (cache: any, state: any) => {
}
} else {
if (!state.hasParseStyle && (state.styles.global.length || state.styles.jsx.length)) {
let isError: any = '';
let globalStyle = '';
let JsxStyle = '';
let styleId = 0;

// 读取缓存
const cacheId = getHashByReference(reference);
const cacheDir = path.join(styleCacheDir, cacheId);
if (!fs.existsSync(cacheDir)) {
fs.mkdirSync(cacheDir);
}

// 读取文件名称
// global、jsx文件的变更时间
let filename = '';
Expand All @@ -71,8 +64,13 @@ export default (cache: any, state: any) => {
from: item
};
});
const newFileName = md5(filename);
const cacheDir = path.join(constant.CACHE_DIR, newFileName.substr(0, 2));
if (!fs.existsSync(cacheDir)) {
fs.mkdirSync(cacheDir);
}
const filepath = path.join(cacheDir, cacheId + '-' + newFileName);

const filepath = path.join(cacheDir, md5(filename));
if (
fs.existsSync(filepath) &&
!global.style_hmr &&
Expand Down Expand Up @@ -109,23 +107,12 @@ export default (cache: any, state: any) => {
clean(cacheDir);
fs.mkdirSync(cacheDir);
}
// 通过loopWhile在同步任务里面执行异步任务
let wait = true;
postcssParse(state)
.then((result: any) => {
globalStyle = result.global;
JsxStyle = result.jsx;
styleId = result.styleId;
wait = false;
})
.catch(err => {
wait = false;
isError = err;
});
loopWhile(() => wait);
if (isError instanceof Error) {
throw isError;
}
// 解析样式资源
const result = postcssParse(state);
globalStyle = result.global;
JsxStyle = result.jsx;
styleId = result.styleId;

if (dev()) {
fs.writeFileSync(
filepath,
Expand Down
127 changes: 54 additions & 73 deletions packages/award-scripts/src/tools/style/postcss-parse/index.ts
Expand Up @@ -38,40 +38,28 @@ const contentByString = (str: any, filepath: any) => {

// postcss批量处理
const handleStyleByPostcss = (styles: any, _plugins: any, isGlobal: any) => {
return new Promise((resolve, reject) => {
if (styles.length) {
Promise.all(
styles.map(async (item: any) => {
// 解析css样式
const { css } = await postcss(_plugins).process(item.css, {
from: item.from
});
// 压缩css文件
const output = new CleanCSS({}).minify(css);
if (isGlobal && !dev()) {
const globalId = DefaultHashString(output.styles);
// 筛选出重复的全局样式引用
if (StoreGlobalStyle.indexOf(globalId) === -1) {
StoreGlobalStyle.push(globalId);
return output.styles;
} else {
return '';
}
} else {
return output.styles;
}
})
)
.then(style => {
resolve(style.join(''));
})
.catch(error => {
reject(error);
});
} else {
resolve('');
}
});
let styleSheet = '';
if (styles.length) {
styles.map(async (item: any) => {
// 解析css样式
const css = postcss(_plugins).process(item.css, {
from: item.from
}).css;
// 压缩css文件
const output = new CleanCSS({}).minify(css);
if (isGlobal && !dev()) {
const globalId = DefaultHashString(output.styles);
// 筛选出重复的全局样式引用
if (StoreGlobalStyle.indexOf(globalId) === -1) {
StoreGlobalStyle.push(globalId);
styleSheet += output.styles;
}
} else {
styleSheet += output.styles;
}
});
}
return styleSheet;
};

/**
Expand Down Expand Up @@ -186,45 +174,38 @@ export default (state: any) => {
})
];

return new Promise(async (resolve, reject) => {
try {
// sass读取样式资源文件内容,并重新赋值state.styles的属性
state.styles.jsx.map((item: any, index: number) => {
state.styles.jsx[index].css = !/\.(j|t)sx?$/.test(item.from)
? contentByFilePath(item.from)
: contentByString(state.scopeCSS, item.from);
});

state.styles.global.map((item: any, index: number) => {
state.styles.global[index].css = !/\.(j|t)sx?$/.test(item.from)
? contentByFilePath(item.from)
: contentByString(state.globalCSS, item.from);
});

// 需要对全局样式的选择器进行过滤识别处理,即不能携带和scope一致的选择器
const globalStyle = await handleStyleByPostcss(state.styles.global, _plugins, true);
let jsxStyle: any = await handleStyleByPostcss(state.styles.jsx, _plugins, false);
let styleId = jsxStyle ? getHashByReference(reference) : 0;

if (styleId) {
if (dev()) {
// 在开发阶段,为了防止热更新时样式不生效,需要带上随机的hash码
styleId = styleId + DefaultHashString(jsxStyle);
}
// 拼接scopeId
const { css } = await postcss([postcssSelector(styleId)]).process(jsxStyle, {
from: undefined
});
jsxStyle = css;
}
// sass读取样式资源文件内容,并重新赋值state.styles的属性
state.styles.jsx.map((item: any, index: number) => {
state.styles.jsx[index].css = !/\.(j|t)sx?$/.test(item.from)
? contentByFilePath(item.from)
: contentByString(state.scopeCSS, item.from);
});

resolve({
global: globalStyle,
jsx: jsxStyle,
styleId
});
} catch (err) {
reject(err);
}
state.styles.global.map((item: any, index: number) => {
state.styles.global[index].css = !/\.(j|t)sx?$/.test(item.from)
? contentByFilePath(item.from)
: contentByString(state.globalCSS, item.from);
});

// 需要对全局样式的选择器进行过滤识别处理,即不能携带和scope一致的选择器
const globalStyle = handleStyleByPostcss(state.styles.global, _plugins, true);
let jsxStyle: any = handleStyleByPostcss(state.styles.jsx, _plugins, false);
let styleId = jsxStyle ? getHashByReference(reference) : 0;

if (styleId) {
if (dev()) {
// 在开发阶段,为了防止热更新时样式不生效,需要带上随机的hash码
styleId = styleId + DefaultHashString(jsxStyle);
}
// 拼接scopeId
jsxStyle = postcss([postcssSelector(styleId)]).process(jsxStyle, {
from: undefined
}).css;
}

return {
global: globalStyle,
jsx: jsxStyle,
styleId
};
};

0 comments on commit 6fdaaab

Please sign in to comment.