欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Webpack的配置案例-devServer、EsLint与多页面

程序员文章站 2022-07-12 19:52:54
...

1. devServer

这里讲解两个devServer相关的配置案例

实现请求转发

问题发现

这里我们基于React尝试发送一个Ajax请求,这里先借助axios库来实现:

axios.get('http://www.joern-lee.com/react/api/header.json').then((res) => {
 ...   
})

这里正常情况下访问一些服务都会遇到跨域问题,另一方面在测试环境下,有时候请求的后端地址其实是不一样的,所以在代码中把host写死的话会很不灵活。

所以通常我们在Ajax都会写一个相对路径而非绝对路径:

axios.get('/react/api/header.json')

但是如何请求到真正的地址呢?一种方法是借助本地类似charles等工具来进行转发。另一种其实可以借助devServer来进行

devServer的proxy

devServer的转发功能主要通过配置proxy字段来实现,例如我们希望将上述/react/api/xxx转发到指定地址,通过proxy就可以解决。

首先我们不使用proxy,直接访问,正常会报如下log:

GET http://localhost:8080/react/api/header.json 404

然后我们这样配置一样:

devServer:{
    ...
    proxy:{
        '/react/api':'http://wwww.joern-lee.com'
    }
}

接着再通过chrome的devtools看一下请求内容,虽然请求的url仍然是localhost,但是经过转发可以获取数据了(上面的proxy地址可以填你自己可以访问的接口测试)

有时候开发阶段,后端会给你一个demo数据的接口来代替header.json,如果你直接改代码,最后上线还要改回来,我们可以通过配置proxy来解决:

proxy:{
     // 如果请求了/react/api这个地址,首先会去http://www.dell-lee.com下面拿数据
     // 那header.json数据时候会拿demo.json返回给你
     // 这样就不用改代码测试,不需要变源代码
        '/react/api':{
            target:'http://wwww.joern-lee.com',
            pathRewrite:{
                'header.json':'demo.json' 
                
            }
        }
    }

更多配置

针对https请求需要加一个secure参数:

proxy:{
        '/react/api':{
            target:'http://wwww.joern-lee.com',
            secure:false
        }
    }

这里我们还可以拦截请求,做一些中间处理:

proxy:{
        '/react/api':{
            target:'http://wwww.joern-lee.com',
            secure:false,
            bypass:function(req,res,proxyOptions){
                process();
            }
        }
    }

希望多个路径进行同一处理:

proxy:[{
    context:['/react/auth','/react/api'],
    target:...
}]

有一些网站对Origin做了限制,防止外部爬虫,这里可以通过这个字段绕过限制:

changeOrigin:true

注意事项

proxy是devServer的代理,只在开发环境下有效,打包上线之后这种代理就不存在了,不会有请求转发!这一点需要注意,主要方便开发环境进行

解决单页面应用路由问题

这里我们以一个React的路由为例来介绍devServer的一个案例

问题引入

这里我们写一个基础的react路由:

...

class App extends Component{
    render(){
        return(
            <BrowserRouter>
                <div>
                    <Route path='/' component={Home}/>
                    <Route path='/list' component={List}/>
                </div>
            </BrowserRouter>
        )
    }
}
...

这里的路由会根据具体的path来决定渲染的组件,以之前的webpack配置文件打包,然后我们看一下浏览器。

正常情况下Home组件可以正常展示,但是path是list时候就不行了。

这是因为BrowserRouter路由器借助了H5 history相关的API,所以当我们在浏览器输入list时,浏览器会尝试访问服务器获取/list下的资源,但其实该路径是不存在的,所以这里也是单页面应用会遇到的一个问题

使用devServer解决

这里只需要加一个historyApiFallback参数就可以了:

devServer:{
    historyApiFallback:true
}

配置的该参数之后,如果浏览器找不到界面就返回默认首页index.html

原理是当服务器发现没有list地址的时候,会偷偷把针对这个路径的请求转化到根路径的请求,你可以发现它其实加载的都是index.html的内容

该参数下面还可以配置from和to字段,来自定义转化的路径:

// 下面配置等价于默认的配置
historyApiFallback:{
    rewrites:[{
        from:/\.*/,
        to:'/index.html'
    }]
}

2. EsLint的配置

简单介绍

团队中,有时希望能够管理大家的编码习惯,使得编程风格尽可能统一,否则同一段逻辑的写法可能都不一样,不好维护。

这里就需要引入EsLint这套代码规范的约束工具

我们可以通过npm进行安装:

npm install eslint --save-dev

之后还需要配置eslint来配置具体规范,这里可以输入命令来初始化:

npx eslint --init

然后根据控制台的配置流程来帮助你生成配置文件.eslintrc.js

eslint具体的使用和配置这里就不展开说了,一般来说可以结合babel-eslint解析器,或者给你的IDE安装插件来使用

但是如果你是通过在IDE装插件才完成ESLint高亮展示,此时其他开发者没有安装该插件,那他怎么看到代码语法提示呢?

所以单纯靠插件来确保质量会有问题(独立开发就没关系了),你没办法每个人都确保安装了插件。使用命令行工具又很麻烦。

这里就可以在webpack中把esLint整合进去

webpack中使用

这里我们可以安装一个loader来处理:

npm install eslint-loader --save-dev

然后修改webapck配置文件:

rules:[...,{
    test:/\.js$/,
    exclude:/node_modules/,
    use:['babel-loader','eslint-loader']
}}}]

这里注意顺序,首先通过eslint-loader来规范代码,然后再进行代码转义。如果转义再检查那么就不对了

需要配置devServer一个配置,设置overlay,这样就可以在不符合规范时弹窗进行提示,这里就把两者结合起来:

devServer:{
    overlay:true
}

接着存在违反eslint的代码编写时,就会在浏览器上弹出一个浮层提示异常。

即便编辑器没有插件也可以很快定义问题。

另外还有cache配置,可以介绍每一次打包时候解析文件时候损耗的速度,提高一点打包速度

最佳实践

公司里面使用EsLint通常不会使用eslint-loader,因为会影响打包速度。

通常在准备提交代码到git仓库的时候,通过git的一些钩子对代码进行eslint检查,执行eslint src,如果不通过规范,禁止你提交代码。

如果对打包速度影响较大,可以扩展一些其他思路,即便牺牲一些便捷性

3. 多页面打包配置案例

之前的打包基本都是针对SPA应用进行,整个页面只有一个HTML文件,这主要是考虑到现在主流前端开发都是基于react,vue等的单页面应用。

但是在部分情况下,例如对老项目兼容,就可能需要处理多页面的打包了

实验环境搭建

我们新建一个首页和列表页:

....

class App extends Component{
    ...
    <div>home page</div>
}

ReactDom.render(<App/>,document.getElementById('root'));
....

class App extends Component{
    ...
    <div>list page</div>
}

ReactDom.render(<App/>,document.getElementById('root'));

基于多页面应用那么上述两个组件就不是父子关系,而是用于两个HTML文件的root节点,这里我们需要先修改配置文件以打包这两个JS文件:

module.exports = {
    entry:{
        main:'./src/index.js',
        list:'./src/list.js'
    }
}

打包完成后,dist目录下面会分别有list和main的打包文件,但是目前打包生成的html文件还是只有一个(通过两个script标签引入了组件)

生成多个HTML文件

这里我们希望生成多个HTML文件且各自分别引用一个JS文件,这里就可以借助前述的HtmlWebpackPlugin插件,新增一个打包产出的HTML

这里我们可以去github上面看一些这个插件文档,多配置一个Plugin:

const plugins = [{
    new HtmlWebpackPlugin({
        template:'src/index.html',
        filename:'index.html',
        chunks:['runtime','vendors','main']
    }),
    new HtmlWebpackPlugin({
        template:'src/index.html',
        filename:'list.html',
        chunks:['runtime','vendors','main']
    })
    new CleanWebpackPlugin...
}]

上述配置了两个Html插件,用于处理多个html文件,模板html都是同一个,filename决定了打包后的文件名称,chunks决定了html引入的包文件。

然后重新打包就可以看到html正确引入了对应的JS文件

自动化生成多HTMLPlugin

上述过程如果涉及HTML很多的话我们需要写很多new HtmlWebpackPlugin的模板代码,这里我们可以写一个解析函数,根据entry字段来生成plugin数组:

const makePlugins = (configs) =>{
    const plugins = [
        new CleanWebpackPlugin...
    ];
    Object.keys(configs.entry).forEach(item => {
        new HtmlWebapckPlugin({
            template:'src/index.html',
            filename:`${item}.html`,
            chunks:['runtime','vendors',item]
        })
    })
}

当你需要新增一个页面,只需要改config配置文件的entry字段就可以