├── .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 |
32 |
内部组件
33 |
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 | {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 | 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 => 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 |
236 |
内部组件
237 |
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. 组件中包含