├── .gitignore
├── views
├── loadable
│ ├── errorComp.jsx
│ ├── box.jsx
│ ├── errorBoundary.jsx
│ ├── app.jsx
│ └── index.ejs
└── production
│ ├── errorComp.jsx
│ ├── box.jsx
│ ├── differentParent.jsx
│ ├── errorBoundary.jsx
│ ├── index.ejs
│ ├── app.jsx
│ ├── wrapBox.jsx
│ ├── innerBox.jsx
│ ├── createContext.jsx
│ ├── changDom.jsx
│ ├── insertDom.jsx
│ ├── selectBox.jsx
│ └── fixInsertDom.jsx
├── biz
└── server.js
├── bin
├── dev.js
├── react.js
├── buildPreview.js
└── reactPreview.js
├── package.json
├── webpack.react.config.js
├── webpack.prod.react.config.js
├── webpack.prod.config.js
├── webpack.config.js
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | node_modules/*
3 | package-lock.json
4 | devtmp
5 | dist
--------------------------------------------------------------------------------
/views/loadable/errorComp.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDom from 'react-dom';
3 |
4 | export default class ErrorComp extends React.PureComponent {
5 | render() {
6 | return
{this.props.person.name}
;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/views/production/errorComp.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDom from 'react-dom';
3 |
4 | export default class ErrorComp extends React.PureComponent {
5 | render() {
6 | return {this.props.person.name}
;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/views/loadable/box.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDom from 'react-dom';
3 |
4 | export default class Box extends React.PureComponent {
5 | render() {
6 | const { title } = this.props.content;
7 | return {title} async load box
;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/biz/server.js:
--------------------------------------------------------------------------------
1 | const Koa = require('koa');
2 | const koaStatic = require('koa-static');
3 | const path = require('path');
4 | const app = new Koa();
5 | const argv = require('yargs').argv;
6 |
7 | const server = app.listen(8087);
8 | const column = argv.env === 'dev' ? '../devtmp' : '../dist';
9 |
10 | app.use(koaStatic(path.resolve(__dirname, column)));
11 |
--------------------------------------------------------------------------------
/bin/dev.js:
--------------------------------------------------------------------------------
1 | const shell = require('shelljs');
2 | const path = require('path');
3 | const del = require('del');
4 |
5 | let arr = del.sync([path.join(__dirname + '/../devtmp/**')]);
6 | console.log('正在删除目录');
7 |
8 | shell.exec('node ./biz/server.js --env=dev', { async: true }, (code, stdout, stderr) => {});
9 | shell.exec('webpack --watch', { async: true }, (code, stdout, stderr) => {});
10 |
--------------------------------------------------------------------------------
/bin/react.js:
--------------------------------------------------------------------------------
1 | const shell = require('shelljs');
2 | const path = require('path');
3 | const del = require('del');
4 |
5 | let arr = del.sync([path.join(__dirname + '/../devtmp/**')]);
6 | console.log('正在删除目录');
7 |
8 | shell.exec('node ./biz/server.js --env=dev', { async: true }, (code, stdout, stderr) => {});
9 | shell.exec('webpack --config ./webpack.react.config --watch', { async: true }, (code, stdout, stderr) => {});
10 |
--------------------------------------------------------------------------------
/bin/buildPreview.js:
--------------------------------------------------------------------------------
1 | const shell = require('shelljs');
2 | const path = require('path');
3 | const del = require('del');
4 |
5 | let arr = del.sync([path.join(__dirname + '/../dist/**')]);
6 | console.log('正在删除目录');
7 |
8 | shell.exec('node ./biz/server.js --env=build', { async: true }, (code, stdout, stderr) => {});
9 | shell.exec('webpack --config ./webpack.prod.config --watch', { async: true }, (code, stdout, stderr) => {});
10 |
--------------------------------------------------------------------------------
/bin/reactPreview.js:
--------------------------------------------------------------------------------
1 | const shell = require('shelljs');
2 | const path = require('path');
3 | const del = require('del');
4 |
5 | let arr = del.sync([path.join(__dirname + '/../dist/**')]);
6 | console.log('正在删除目录');
7 |
8 | shell.exec('node ./biz/server.js --env=build', { async: true }, (code, stdout, stderr) => {});
9 | shell.exec('webpack --config ./webpack.prod.react.config --watch', { async: true }, (code, stdout, stderr) => {});
10 |
--------------------------------------------------------------------------------
/views/production/box.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDom from 'react-dom';
3 | import DifferentParent from './differentParent';
4 |
5 | export default class Box extends React.PureComponent {
6 | componentDidMount() {
7 | console.log('box componentDidMount trigger');
8 | }
9 | render() {
10 | return (
11 |
12 |
13 |
14 | );
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/views/production/differentParent.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDom from 'react-dom';
3 |
4 | export default class DifferentParent extends React.PureComponent {
5 | box = React.createRef();
6 |
7 | componentDidMount() {
8 | console.log('react 的行为是获取的父元素是一致的', this.box.current.parentElement.offsetHeight);
9 | setTimeout(() => {
10 | console.log('async', this.box.current.parentElement.offsetHeight);
11 | }, 100);
12 | }
13 | render() {
14 | return box
;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/views/loadable/errorBoundary.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDom from 'react-dom';
3 |
4 | export default class ErrorBoundary extends React.PureComponent {
5 | state = {
6 | haseError: false,
7 | };
8 | componentDidCatch(error, info) {
9 | console.log(error);
10 | console.log('--------');
11 | console.log(info);
12 | this.setState({
13 | haseError: true,
14 | });
15 | }
16 | render() {
17 | if (this.state.haseError) {
18 | return this components has a error!
;
19 | } else {
20 | return this.props.children;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/views/production/errorBoundary.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDom from 'react-dom';
3 |
4 | export default class ErrorBoundary extends React.PureComponent {
5 | state = {
6 | haseError: false,
7 | };
8 | componentDidCatch(error, info) {
9 | console.log(error);
10 | console.log('--------');
11 | console.log(info);
12 | this.setState({
13 | haseError: true,
14 | });
15 | }
16 | render() {
17 | if (this.state.haseError) {
18 | return this components has a error!
;
19 | } else {
20 | return this.props.children;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/views/loadable/app.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import 'core-js';
4 | import Loadable from 'react-loadable';
5 | import ErrorBoundary from './errorBoundary';
6 | import ErrorComp from './errorComp';
7 |
8 | const LoadaBox = Loadable({
9 | loader: () => import('./box'),
10 | loading() {
11 | return Loading...
;
12 | },
13 | });
14 |
15 | const render = function render() {
16 | ReactDOM.render(
17 |
18 |
head
19 |
20 |
foot
21 |
22 |
23 |
24 |
,
25 | document.getElementById('root'),
26 | );
27 | };
28 |
29 | render();
30 |
--------------------------------------------------------------------------------
/views/loadable/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
17 |
18 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/views/production/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
17 |
18 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/views/production/app.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import 'core-js';
4 | import Box from './box';
5 | import ErrorBoundary from './errorBoundary';
6 | import ErrorComp from './errorComp';
7 | import SelectBox from './selectBox';
8 | import WrapBox from './wrapBox';
9 | import ChangeDom from './changDom';
10 | import InsertDom from './insertDom';
11 | import FixInsertDom from './fixInsertDom';
12 | import CreateContext from './createContext';
13 |
14 | const render = function render() {
15 | ReactDOM.render(
16 |
17 |
18 |
19 |
20 |
21 |
22 |
hello anu!
23 |
24 |
25 |
26 |
27 |
28 |
,
29 | document.getElementById('root'),
30 | );
31 | };
32 |
33 | render();
34 |
--------------------------------------------------------------------------------
/views/production/wrapBox.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import InnerBox from './innerBox';
3 |
4 | class WrapBox extends React.PureComponent {
5 | state = {
6 | value: 1,
7 | };
8 |
9 | componentDidMount() {
10 | console.log('WrapBox render');
11 | // 此处如果在组件创建以后,再次setState,会导致dom位置错误的现象发生
12 | setTimeout(() => {
13 | console.log('父容器state值改变');
14 | this.setState({
15 | value: 1,
16 | });
17 | }, 700);
18 | }
19 |
20 | /**
21 | * 渲染组件
22 | */
23 | render() {
24 | console.log('wrap render trigger');
25 | return (
26 |
27 |
标题区域1
28 |
标题区域2
29 |
30 |
标题区域3
31 |
32 | );
33 | }
34 | }
35 |
36 | export default WrapBox;
37 |
--------------------------------------------------------------------------------
/views/production/innerBox.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | class InnerBox extends React.PureComponent {
4 | state = {
5 | current: 0,
6 | };
7 |
8 | componentDidMount() {
9 | console.log('InnerBox render');
10 | setTimeout(() => {
11 | console.log('current跟上次不一样,一切正常');
12 | this.setState({ current: 1 });
13 | }, 500);
14 | setTimeout(() => {
15 | console.log('current跟上次一样,dom位置互换');
16 | this.setState({ current: 1 });
17 | }, 1000);
18 | setTimeout(() => {
19 | console.log('current跟上次不一样,dom位置复原');
20 | this.setState({ current: 2 });
21 | }, 1500);
22 | setTimeout(() => {
23 | console.log('current跟上次一样,dom位置互换');
24 | this.setState({ current: 2 });
25 | }, 2000);
26 | }
27 |
28 | render() {
29 | console.log('inner render trigger');
30 | return (
31 |
34 | );
35 | }
36 | }
37 |
38 | export default InnerBox;
39 |
--------------------------------------------------------------------------------
/views/production/createContext.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDom from 'react-dom';
3 |
4 | const ThemeContext = React.createContext('light');
5 |
6 | export default class CreateContext extends React.Component {
7 | render() {
8 | // Use a Provider to pass the current theme to the tree below.
9 | // Any component can read it, no matter how deep it is.
10 | // In this example, we're passing "dark" as the current value.
11 | return (
12 |
13 |
14 |
15 | );
16 | }
17 | }
18 |
19 | // A component in the middle doesn't have to
20 | // pass the theme down explicitly anymore.
21 | function Toolbar(props) {
22 | return (
23 |
24 |
25 |
26 | );
27 | }
28 |
29 | function ThemedButton(props) {
30 | // Use a Consumer to read the current theme context.
31 | // React will find the closest theme Provider above and use its value.
32 | // In this example, the current theme is "dark".
33 | return {theme => } ;
34 | }
35 |
36 | function Button(props) {
37 | return (
38 |
39 | 正确值应该是传入的dark,现在传入值为:
40 | {props.theme}
41 |
42 | );
43 | }
44 |
--------------------------------------------------------------------------------
/views/production/changDom.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDom from 'react-dom';
3 |
4 | export default class ChangeDom extends React.PureComponent {
5 | state = {
6 | list: [],
7 | };
8 | componentDidMount() {
9 | console.log('box componentDidMount trigger');
10 | }
11 |
12 | renderSpan = () => {
13 | const { list } = this.state;
14 | const last = list[list.length - 1];
15 | document.getElementById(`box${last}`).innerHTML = `update_${last}`;
16 | };
17 | clickHandle = () => {
18 | this.setState(
19 | {
20 | list: [...this.state.list, this.state.list.length],
21 | },
22 | () => {
23 | this.renderSpan();
24 | },
25 | );
26 | };
27 | render() {
28 | const { list } = this.state;
29 |
30 | return (
31 |
32 |
修改已有dom的内容导致ie下面出错
33 |
点击增加数据
34 | {list.map((item, index) => {
35 | return (
36 |
37 | {item}:0
38 |
39 | );
40 | })}
41 |
42 | );
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/views/production/insertDom.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDom from 'react-dom';
3 |
4 | export default class InsertDom extends React.PureComponent {
5 | state = {
6 | list: [],
7 | };
8 | componentDidMount() {
9 | console.log('box componentDidMount trigger');
10 | }
11 |
12 | renderSpan = () => {
13 | const { list } = this.state;
14 | const last = list[list.length - 1];
15 | const dom = document.createElement('div');
16 | dom.innerHTML = `insert_${last}`;
17 | const div = document.getElementById(`list_${last}`);
18 | div.parentElement.insertBefore(dom, div);
19 | };
20 | clickHandle = () => {
21 | this.setState(
22 | {
23 | list: [...this.state.list, this.state.list.length],
24 | },
25 | () => {
26 | this.renderSpan();
27 | },
28 | );
29 | };
30 | render() {
31 | const { list } = this.state;
32 |
33 | return (
34 |
35 |
在列表中插通过dom操作插入数据
36 |
点击增加数据
37 | {list.map((item, index) => {
38 | return (
39 |
40 | {item}:0
41 |
42 | );
43 | })}
44 |
45 | );
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "anujs-webpack4-ie7-8",
3 | "version": "1.0.0",
4 | "description": "基于anujs和webpack4兼容ie7-8的配置",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node ./bin/dev.js",
8 | "react": "node ./bin/react.js",
9 | "buildPreview": "node ./bin/buildPreview.js",
10 | "reactPreview": "node ./bin/reactPreview.js",
11 | "test": "echo \"Error: no test specified\" && exit 1"
12 | },
13 | "repository": {
14 | "type": "git",
15 | "url": "git+https://github.com/magicapple/anujs-webpack4-ie7-8.git"
16 | },
17 | "author": "",
18 | "license": "ISC",
19 | "bugs": {
20 | "url": "https://github.com/magicapple/anujs-webpack4-ie7-8/issues"
21 | },
22 | "homepage": "https://github.com/magicapple/anujs-webpack4-ie7-8#readme",
23 | "dependencies": {
24 | "anujs": "^1.4.7",
25 | "react": "^16.4.0",
26 | "react-dom": "^16.4.0",
27 | "react-loadable": "^5.4.0"
28 | },
29 | "devDependencies": {
30 | "argv": "0.0.2",
31 | "babel-core": "^6.26.3",
32 | "babel-loader": "^7.1.4",
33 | "babel-plugin-transform-runtime": "^6.23.0",
34 | "babel-polyfill": "^6.26.0",
35 | "babel-preset-env": "^1.7.0",
36 | "babel-preset-react": "^6.24.1",
37 | "babel-preset-stage-2": "^6.24.1",
38 | "core-js": "^2.5.7",
39 | "css-loader": "^0.28.11",
40 | "del": "^3.0.0",
41 | "es3ify-webpack-plugin": "0.0.1",
42 | "html-webpack-plugin": "^4.0.0-alpha",
43 | "koa": "^2.5.1",
44 | "koa-static": "^4.0.3",
45 | "shelljs": "^0.8.2",
46 | "style-loader": "^0.21.0",
47 | "uglifyjs-webpack-plugin": "^1.2.5",
48 | "url-loader": "^1.0.1",
49 | "webpack": "^4.12.0",
50 | "webpack-cli": "^3.0.3"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/views/production/selectBox.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDom from 'react-dom';
3 |
4 | export default class SelectBox extends React.PureComponent {
5 | data = [{ name: 'a', text: 'aaaa' }, { name: 'b', text: 'bbbb' }, { name: 'c', text: 'cccc' }];
6 | state = {
7 | current: 0,
8 | };
9 | handlerChange = e => {
10 | console.log(e.currentTarget.value);
11 | const index = e.currentTarget.value;
12 | this.setState({
13 | current: index,
14 | });
15 | };
16 |
17 | handlerClick = e => {
18 | console.log(e.currentTarget);
19 | const index = e.currentTarget.getAttribute('value');
20 | this.setState({
21 | current: index,
22 | });
23 | };
24 | render() {
25 | const data = this.data;
26 | const { current } = this.state;
27 | return (
28 |
29 |
30 | {data.map((item, index) => (
31 |
42 | {item.name}
43 |
44 | ))}
45 |
46 |
47 | a
48 | b
49 | c
50 |
51 |
{data[current].text}
52 |
53 | );
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/views/production/fixInsertDom.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDom from 'react-dom';
3 |
4 | export default class FixInsertDom extends React.PureComponent {
5 | state = {
6 | list: [],
7 | };
8 | componentDidMount() {
9 | console.log('box componentDidMount trigger');
10 | }
11 |
12 | renderAd = () => {
13 | const { list } = this.state;
14 | console.log('==========');
15 | console.log(list.length - 1);
16 | const last = list[list.length - 1];
17 | const dom = document.createElement('div');
18 | dom.innerHTML = `ad_${last.id}`;
19 | const div = document.getElementById(`ad${last.id}`);
20 | console.log(div);
21 | div.appendChild(dom);
22 | };
23 |
24 | insertAd = () => {
25 | this.setState(
26 | {
27 | list: [...this.state.list, { id: this.state.list.length, type: 'ad' }],
28 | },
29 | () => {
30 | this.renderAd();
31 | },
32 | );
33 | };
34 | clickHandle = () => {
35 | this.setState(
36 | {
37 | list: [...this.state.list, { id: this.state.list.length, type: 'content' }],
38 | },
39 | () => {
40 | this.insertAd();
41 | },
42 | );
43 | };
44 | render() {
45 | const { list } = this.state;
46 | console.log(list);
47 |
48 | return (
49 |
50 |
修复在列表中插通过dom操作插入数据
51 |
点击增加数据
52 | {list.map((item, index) => {
53 | console.log(item.type);
54 | return item.type === 'ad' ? (
55 |
56 | ) : (
57 |
58 | {item.id}:0
59 |
60 | );
61 | })}
62 |
63 | );
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/webpack.react.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const es3ifyPlugin = require('es3ify-webpack-plugin');
4 | const HtmlWebpackPlugin = require('html-webpack-plugin');
5 |
6 | module.exports = {
7 | devtool: 'cheap-module-source-map',
8 | entry: {
9 | production: path.resolve(__dirname, './views/production/app.jsx'),
10 | },
11 | output: {
12 | path: path.resolve(__dirname, 'devtmp'),
13 | filename: 'js/[name].js',
14 | publicPath: '/',
15 | chunkFilename: 'js/[name].js',
16 | },
17 | resolve: {
18 | extensions: ['.js', '.json', '.jsx'],
19 | },
20 | module: {
21 | rules: [
22 | {
23 | test: /\.jsx?$/,
24 | use: {
25 | loader: 'babel-loader',
26 | options: {
27 | presets: [
28 | [
29 | 'env',
30 | {
31 | targets: {
32 | browsers: ['last 2 versions', 'ie >= 7'],
33 | },
34 | modules: 'commonjs',
35 | useBuiltIns: true,
36 | debug: false,
37 | },
38 | ],
39 | 'react',
40 | 'stage-2',
41 | ],
42 | plugins: ['transform-runtime'],
43 | },
44 | },
45 | include: [path.resolve(__dirname, 'views')],
46 | },
47 | {
48 | test: /\.css$/,
49 | include: [path.resolve(__dirname, 'views')],
50 | use: ['style-loader', 'css-loade'],
51 | },
52 | {
53 | test: /\.(eot|woff|woff2|ttf|svg|png|jpg|gif)$/,
54 | use: [
55 | {
56 | loader: 'url-loader',
57 | options: {
58 | limit: 100,
59 | name: 'asset/[name].[ext]',
60 | },
61 | },
62 | ],
63 | },
64 | ],
65 | },
66 | mode: 'development',
67 | plugins: [
68 | new es3ifyPlugin(),
69 | new HtmlWebpackPlugin({
70 | filename: 'production.html',
71 | template: path.resolve(__dirname, './views/production/index.ejs'),
72 | inject: 'body',
73 | hase: false,
74 | minify: {
75 | // 压缩HTML文件
76 | removeComments: true, // 移除HTML中的注释
77 | collapseWhitespace: false, // 删除空白符与换行符
78 | },
79 | chunks: ['production'],
80 | }),
81 | ],
82 | };
83 |
--------------------------------------------------------------------------------
/webpack.prod.react.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const HtmlWebpackPlugin = require('html-webpack-plugin');
4 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
5 |
6 | module.exports = {
7 | devtool: 'source-map',
8 | entry: {
9 | production: path.resolve(__dirname, './views/production/app.jsx'),
10 | },
11 | output: {
12 | path: path.resolve(__dirname, 'dist'),
13 | filename: 'js/[name].js',
14 | publicPath: '/',
15 | chunkFilename: 'js/[name].js',
16 | },
17 | resolve: {
18 | extensions: ['.js', '.json', '.jsx'],
19 | },
20 | optimization: {
21 | minimizer: [
22 | new UglifyJsPlugin({
23 | uglifyOptions: {
24 | ie8: true,
25 | },
26 | sourceMap: true,
27 | }),
28 | ],
29 | },
30 | module: {
31 | rules: [
32 | {
33 | test: /\.jsx?$/,
34 | use: {
35 | loader: 'babel-loader',
36 | options: {
37 | presets: [
38 | [
39 | 'env',
40 | {
41 | targets: {
42 | browsers: ['last 2 versions', 'ie >= 7'],
43 | },
44 | modules: 'commonjs',
45 | useBuiltIns: true,
46 | debug: false,
47 | },
48 | ],
49 | 'react',
50 | 'stage-2',
51 | ],
52 | plugins: ['transform-runtime'],
53 | },
54 | },
55 | include: [path.resolve(__dirname, 'views')],
56 | },
57 | {
58 | test: /\.css$/,
59 | include: [path.resolve(__dirname, 'views')],
60 | use: ['style-loader', 'css-loade'],
61 | },
62 | {
63 | test: /\.(eot|woff|woff2|ttf|svg|png|jpg|gif)$/,
64 | use: [
65 | {
66 | loader: 'url-loader',
67 | options: {
68 | limit: 100,
69 | name: 'asset/[name].[ext]',
70 | },
71 | },
72 | ],
73 | },
74 | ],
75 | },
76 | mode: 'production',
77 | plugins: [
78 | new HtmlWebpackPlugin({
79 | filename: 'production.html',
80 | template: path.resolve(__dirname, './views/production/index.ejs'),
81 | inject: 'body',
82 | hase: false,
83 | minify: {
84 | // 压缩HTML文件
85 | removeComments: true, // 移除HTML中的注释
86 | collapseWhitespace: false, // 删除空白符与换行符
87 | },
88 | chunks: ['production'],
89 | }),
90 | ],
91 | };
92 |
--------------------------------------------------------------------------------
/webpack.prod.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const HtmlWebpackPlugin = require('html-webpack-plugin');
4 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
5 |
6 | module.exports = {
7 | devtool: 'source-map',
8 | entry: {
9 | production: path.resolve(__dirname, './views/production/app.jsx'),
10 | },
11 | output: {
12 | path: path.resolve(__dirname, 'dist'),
13 | filename: 'js/[name].js',
14 | publicPath: '/',
15 | chunkFilename: 'js/[name].js',
16 | },
17 | resolve: {
18 | extensions: ['.js', '.json', '.jsx'],
19 | alias: {
20 | react: 'anujs/dist/ReactIE.js',
21 | 'react-dom': 'anujs/dist/ReactIE.js',
22 | 'prop-types': 'anujs/lib/ReactPropTypes',
23 | devtools: 'anujs/lib/devtools',
24 | 'create-react-class': 'anujs/lib/createClass',
25 | },
26 | },
27 | optimization: {
28 | minimizer: [
29 | new UglifyJsPlugin({
30 | uglifyOptions: {
31 | ie8: true,
32 | },
33 | sourceMap: true,
34 | }),
35 | ],
36 | },
37 | module: {
38 | rules: [
39 | {
40 | test: /\.jsx?$/,
41 | use: {
42 | loader: 'babel-loader',
43 | options: {
44 | presets: [
45 | [
46 | 'env',
47 | {
48 | targets: {
49 | browsers: ['last 2 versions', 'ie >= 7'],
50 | },
51 | modules: 'commonjs',
52 | useBuiltIns: true,
53 | debug: false,
54 | },
55 | ],
56 | 'react',
57 | 'stage-2',
58 | ],
59 | plugins: ['transform-runtime'],
60 | },
61 | },
62 | include: [path.resolve(__dirname, 'views')],
63 | },
64 | {
65 | test: /\.css$/,
66 | include: [path.resolve(__dirname, 'views')],
67 | use: ['style-loader', 'css-loade'],
68 | },
69 | {
70 | test: /\.(eot|woff|woff2|ttf|svg|png|jpg|gif)$/,
71 | use: [
72 | {
73 | loader: 'url-loader',
74 | options: {
75 | limit: 100,
76 | name: 'asset/[name].[ext]',
77 | },
78 | },
79 | ],
80 | },
81 | ],
82 | },
83 | mode: 'production',
84 | plugins: [
85 | new HtmlWebpackPlugin({
86 | filename: 'production.html',
87 | template: path.resolve(__dirname, './views/production/index.ejs'),
88 | inject: 'body',
89 | hase: false,
90 | minify: {
91 | // 压缩HTML文件
92 | removeComments: true, // 移除HTML中的注释
93 | collapseWhitespace: false, // 删除空白符与换行符
94 | },
95 | chunks: ['production'],
96 | }),
97 | ],
98 | };
99 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const es3ifyPlugin = require('es3ify-webpack-plugin');
4 | const HtmlWebpackPlugin = require('html-webpack-plugin');
5 |
6 | module.exports = {
7 | devtool: 'cheap-module-source-map',
8 | entry: {
9 | production: path.resolve(__dirname, './views/production/app.jsx'),
10 | loadable: path.resolve(__dirname, './views/loadable/app.jsx'),
11 | },
12 | output: {
13 | path: path.resolve(__dirname, 'devtmp'),
14 | filename: 'js/[name].js',
15 | publicPath: '/',
16 | chunkFilename: 'js/[name].js',
17 | },
18 | resolve: {
19 | extensions: ['.js', '.json', '.jsx'],
20 | alias: {
21 | react: 'anujs/dist/ReactIE.js',
22 | 'react-dom': 'anujs/dist/ReactIE.js',
23 | 'prop-types': 'anujs/lib/ReactPropTypes',
24 | devtools: 'anujs/lib/devtools',
25 | 'create-react-class': 'anujs/lib/createClass',
26 | },
27 | },
28 | module: {
29 | rules: [
30 | {
31 | test: /\.jsx?$/,
32 | use: {
33 | loader: 'babel-loader',
34 | options: {
35 | presets: [
36 | [
37 | 'env',
38 | {
39 | targets: {
40 | browsers: ['last 2 versions', 'ie >= 7'],
41 | },
42 | modules: 'commonjs',
43 | useBuiltIns: true,
44 | debug: false,
45 | },
46 | ],
47 | 'react',
48 | 'stage-2',
49 | ],
50 | plugins: ['transform-runtime'],
51 | },
52 | },
53 | include: [path.resolve(__dirname, 'views')],
54 | },
55 | {
56 | test: /\.css$/,
57 | include: [path.resolve(__dirname, 'views')],
58 | use: ['style-loader', 'css-loade'],
59 | },
60 | {
61 | test: /\.(eot|woff|woff2|ttf|svg|png|jpg|gif)$/,
62 | use: [
63 | {
64 | loader: 'url-loader',
65 | options: {
66 | limit: 100,
67 | name: 'asset/[name].[ext]',
68 | },
69 | },
70 | ],
71 | },
72 | ],
73 | },
74 | mode: 'development',
75 | plugins: [
76 | new es3ifyPlugin(),
77 | new HtmlWebpackPlugin({
78 | filename: 'production.html',
79 | template: path.resolve(__dirname, './views/production/index.ejs'),
80 | inject: 'body',
81 | hase: false,
82 | minify: {
83 | // 压缩HTML文件
84 | removeComments: true, // 移除HTML中的注释
85 | collapseWhitespace: false, // 删除空白符与换行符
86 | },
87 | chunks: ['production'],
88 | }),
89 | new HtmlWebpackPlugin({
90 | filename: 'loadable.html',
91 | template: path.resolve(__dirname, './views/loadable/index.ejs'),
92 | inject: 'body',
93 | hase: false,
94 | minify: {
95 | // 压缩HTML文件
96 | removeComments: true, // 移除HTML中的注释
97 | collapseWhitespace: false, // 删除空白符与换行符
98 | },
99 | chunks: ['loadable'],
100 | }),
101 | ],
102 | };
103 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # anujs-webpack4-ie7-8
2 |
3 | 基于 anujs 和 webpack4 兼容 ie7-8 的配置
4 |
5 | 该 webpack.config 只考虑了对 ie7 ie8 进行配置。
6 |
7 | ## 使用方法
8 |
9 | ```
10 | // 安装依赖
11 | npm i
12 |
13 | // 开启 development 模式
14 | npm start
15 |
16 | // 开启 production 模式
17 | npm run buildPreview
18 |
19 | // 开启 使用 react库的开发模式
20 | npm run react
21 |
22 | // 开启 使用 react库的生产模式
23 | npm run reactPreview
24 |
25 | // 浏览器访问
26 | http://127.0.0.1:8087/production.html
27 | ```
28 |
29 | ## anujs 1.4.7 发现的一些问题
30 |
31 | 1. React.createContext 无法通过 Provider 传入数据,这个问题是从 1.4.3 开始的,测试了 1.4.1 和 1.4.2 是可以的。
32 |
33 | 代码如下
34 |
35 | ```
36 | import React from 'react';
37 | import ReactDom from 'react-dom';
38 |
39 | const ThemeContext = React.createContext('light');
40 |
41 | export default class CreateContext extends React.Component {
42 | render() {
43 | // Use a Provider to pass the current theme to the tree below.
44 | // Any component can read it, no matter how deep it is.
45 | // In this example, we're passing "dark" as the current value.
46 | return (
47 |
48 |
49 |
50 | );
51 | }
52 | }
53 |
54 | // A component in the middle doesn't have to
55 | // pass the theme down explicitly anymore.
56 | function Toolbar(props) {
57 | return (
58 |
59 |
60 |
61 | );
62 | }
63 |
64 | function ThemedButton(props) {
65 | // Use a Consumer to read the current theme context.
66 | // React will find the closest theme Provider above and use its value.
67 | // In this example, the current theme is "dark".
68 | return {theme => } ;
69 | }
70 |
71 | function Button(props) {
72 | return (
73 |
74 | 正确值应该是传入的dark,现在传入值为:
75 | {props.theme}
76 |
77 | );
78 | }
79 | ```
80 |
81 | ## anujs 1.4.6 发现的一些问题
82 |
83 | > 以下两个问题 采用了先增加容器数据,然后再插入 dom 的方式规避了,具体看 demo 里面
84 |
85 | 1. 在一个组件渲染完成后,通过 dom 操作改变了其中一部分的内容,然后再对组件进行更新,会导致 ie(ie7,8,9,10,11)报错,chrome 下面渲染结果和 react 不一致
86 |
87 | 代码如下
88 |
89 | ```
90 | import React from 'react';
91 | import ReactDom from 'react-dom';
92 |
93 | export default class ChangeDom extends React.PureComponent {
94 | state = {
95 | list: [],
96 | };
97 | componentDidMount() {
98 | console.log('box componentDidMount trigger');
99 | }
100 |
101 | renderSpan = () => {
102 | const { list } = this.state;
103 | const last = list[list.length - 1];
104 | document.getElementById(`box${last}`).innerHTML = `update_${last}`;
105 | };
106 | clickHandle = () => {
107 | this.setState(
108 | {
109 | list: [...this.state.list, this.state.list.length],
110 | },
111 | () => {
112 | this.renderSpan();
113 | },
114 | );
115 | };
116 | render() {
117 | const { list } = this.state;
118 |
119 | return (
120 |
121 |
修改已有dom的内容导致ie下面出错
122 |
点击增加数据
123 | {list.map((item, index) => {
124 | return (
125 |
126 | {item}:0
127 |
128 | );
129 | })}
130 |
131 | );
132 | }
133 | }
134 | ```
135 |
136 | 1. 在一个组件渲染完成后,通过 dom 操作在其中插入了 dom 节点,然后再对组件进行更新,dom 的渲染顺序和 react 的渲染结果不符(chrome,ie 都有)
137 |
138 | 代码如下
139 |
140 | ```
141 | import React from 'react';
142 | import ReactDom from 'react-dom';
143 |
144 | export default class InsertDom extends React.PureComponent {
145 | state = {
146 | list: [],
147 | };
148 | componentDidMount() {
149 | console.log('box componentDidMount trigger');
150 | }
151 |
152 | renderSpan = () => {
153 | const { list } = this.state;
154 | const last = list[list.length - 1];
155 | const dom = document.createElement('div');
156 | dom.innerHTML = `insert_${last}`;
157 | const div = document.getElementById(`list${last}`);
158 | div.parentElement.insertBefore(dom, div);
159 | };
160 | clickHandle = () => {
161 | this.setState(
162 | {
163 | list: [...this.state.list, this.state.list.length],
164 | },
165 | () => {
166 | this.renderSpan();
167 | },
168 | );
169 | };
170 | render() {
171 | const { list } = this.state;
172 |
173 | return (
174 |
175 |
在列表中插通过dom操作插入数据
176 |
点击增加数据
177 | {list.map((item, index) => {
178 | return (
179 |
180 | {item}:0
181 |
182 | );
183 | })}
184 |
185 | );
186 | }
187 | }
188 | ```
189 |
190 | ## anujs 1.4.3 发现的一些问题(已经在 1.4.4 修复了)
191 |
192 | 1. 使用 PureComponent 时,组件两次 setState 值不变并且父组件在此之前也进行过 setState,会导致该组件与其相邻的前面组件的 dom 节点位置互换 (chrom 67, ie7, ie8)
193 |
194 | 该问题出现需要满足如下几个条件:
195 |
196 | > 1. 组件使用 PureComponent
197 | > 2. 父组件进行过 setState(改不改变值都可以)
198 | > 3. 子组件 setState,但是值跟上一次相同
199 | > 4. 子组件前面存在相邻的组件
200 |
201 | 组件代码如下
202 |
203 | ```
204 | // innerBox.jsx
205 | import React from 'react';
206 |
207 | class InnerBox extends React.PureComponent {
208 | state = {
209 | current: 0,
210 | };
211 |
212 | componentDidMount() {
213 | console.log('InnerBox render');
214 | setTimeout(() => {
215 | console.log('current跟上次不一样,一切正常');
216 | this.setState({ current: 1 });
217 | }, 500);
218 | setTimeout(() => {
219 | console.log('current跟上次一样,dom位置互换');
220 | this.setState({ current: 1 });
221 | }, 1000);
222 | setTimeout(() => {
223 | console.log('current跟上次不一样,dom位置复原');
224 | this.setState({ current: 2 });
225 | }, 1500);
226 | setTimeout(() => {
227 | console.log('current跟上次一样,dom位置互换');
228 | this.setState({ current: 2 });
229 | }, 2000);
230 | }
231 |
232 | render() {
233 | console.log('inner render trigger');
234 | return (
235 |
238 | );
239 | }
240 | }
241 |
242 | export default InnerBox;
243 |
244 | // wrapBox
245 |
246 | import React from 'react';
247 | import InnerBox from './innerBox';
248 |
249 | class WrapBox extends React.PureComponent {
250 | state = {
251 | value: 1,
252 | };
253 |
254 | componentDidMount() {
255 | console.log('WrapBox render');
256 | // 此处如果在组件创建以后,再次setState,会导致dom位置错误的现象发生
257 | setTimeout(() => {
258 | console.log('父容器state值改变');
259 | this.setState({
260 | value: 1,
261 | });
262 | }, 700);
263 | }
264 |
265 | /**
266 | * 渲染组件
267 | */
268 | render() {
269 | console.log('wrap render trigger');
270 | return (
271 |
272 |
标题区域1
273 |
标题区域2
274 |
275 |
标题区域3
276 |
277 | );
278 | }
279 | }
280 |
281 | export default WrapBox;
282 | ```
283 |
284 | ## anujs 1.4.2 发现的一些问题(都已经在 1.4.3 修复了)
285 |
286 | 1. 在 webpack 的 `production` mode 下面,代码执行有错误,(chrome 66,ie7,ie8)
287 |
288 | ```
289 | // dist/ReactIE.js 84
290 |
291 | function miniCreateClass(ctor, superClass, methods, statics) {
292 | var className = ctor.name || String(ctor).match(/^function\s(\w+)/)[1];
293 | var Ctor = Function("superClass", "ctor", "return function " + className + " (props, context) {\n superClass.apply(this, arguments); \n ctor.apply(this, arguments);\n }")(superClass, ctor);
294 | Ctor.displayName = className;
295 | var fn = inherit(Ctor, superClass);
296 | extend(fn, methods);
297 | if (statics) {
298 | extend(Ctor, statics);
299 | }
300 | return Ctor;
301 | }
302 | ```
303 |
304 | 2. 在使用 ref 的时候,表现和 react 不一致
305 |
306 | ```
307 | export default class DifferentParent extends React.PureComponent {
308 | box = React.createRef();
309 |
310 | componentDidMount() {
311 | console.log('react 的行为是获取的父元素高度是一致的,anujs是不一致的,', this.box.current.parentElement.offsetHeight);
312 | setTimeout(() => {
313 | console.log('async', this.box.current.parentElement.offsetHeight);
314 | }, 100);
315 | }
316 | render() {
317 | return box
;
318 | }
319 | }
320 | ```
321 |
322 | 3. 组件中包含元素的时候,如果组件被再次 render,在 ie7、ie8 下面报错
323 |
324 | ```
325 | export default class SelectBox extends React.PureComponent {
326 | data = [{ name: 'a', text: 'aaaa' }, { name: 'b', text: 'bbbb' }, { name: 'c', text: 'cccc' }];
327 | state = {
328 | current: 0,
329 | };
330 | handlerChange = e => {
331 | console.log(e.currentTarget.value);
332 | const index = e.currentTarget.value;
333 | this.setState({
334 | current: index,
335 | });
336 | };
337 |
338 | handlerClick = e => {
339 | console.log(e.currentTarget);
340 | const index = e.currentTarget.getAttribute('value');
341 | this.setState({
342 | current: index,
343 | });
344 | };
345 | render() {
346 | const data = this.data;
347 | const { current } = this.state;
348 | return (
349 |
350 |
351 | {data.map((item, index) => (
352 |
363 | {item.name}
364 |
365 | ))}
366 |
367 |
368 | a
369 | b
370 | c
371 |
372 |
{data[current].text}
373 |
374 | );
375 | }
376 | }
377 | ```
378 |
379 | `SCRIPT87: 参数无效。报错代码如下`
380 |
381 | ```
382 | function insertElement(fiber) {
383 | var dom = fiber.stateNode,
384 | parent = fiber.parent;
385 | try {
386 | var insertPoint = fiber.forwardFiber ? fiber.forwardFiber.stateNode : null;
387 | var after = insertPoint ? insertPoint.nextSibling : parent.firstChild;
388 | if (after == dom) {
389 | return;
390 | }
391 | if (after === null && dom === parent.lastChild) {
392 | return;
393 | }
394 | Renderer.inserting = fiber.tag === 5 && document.activeElement;
395 | parent.insertBefore(dom, after);
396 | Renderer.inserting = null;
397 | } catch (e) {
398 | throw e;
399 | }
400 | }
401 | ```
402 |
403 | 执行到
404 |
405 | parent.insertBefore(dom, after); 出错,出错时
406 |
407 | parent 为 DispHTMLOptionElement
408 |
409 | dom 为 DispHTMLDOMTextNode
410 |
411 | after 为 DispHTMLDOMTextNode
412 |
--------------------------------------------------------------------------------