妈妈不再用担忧我不会webpack了2

前言

以前写了一篇妈妈不再用担忧我不会webpack了,此次继续对其进行补充。本文依旧是遵循直观易懂的规则进行书写。但愿对你们有帮助。若是不太熟悉webpack能够先看看以前的文章妈妈不再用担忧我不会webpack了。下面咱们由浅入深来介绍webpack的使用javascript

一些你不知道是什么意思的东西(题外话)

import path = require('path');css

  • path.join
  • path.resolve
  • __dirname

path实际上是node中的一个模块,下面咱们就将一下这几个常见的东西。html

path.join

path.join实际上是对路径进行拼接。vue

const path = require('path');

let str1 = path.join('./path/./', './upload', '/file', '123.jpg');
console.log(str1); // path/upload/file/123.jpg

let str2 = path.join('path', 'upload', 'file', '123.jpg');
console.log(str2); // path/upload/file/123.jpg复制代码

使用了它以后就获得了一个拼接好的路径java

path.resolve

它是绝对路径的操做node

let myPath = path.resolve('path1', 'path2', 'a');
console.log(myPath); // E:/workspace/NodeJS/path1/path2/a复制代码

它的结果是绝对路径,不懂绝对路径和相对路径的同窗去查下相关知识哦。
这部分到这就结束了,下面可能涉及到这几个东西的使用。react

__dirname

使用__dirname变量获取当前模块文件所在目录的完整绝对路径
由于下面可能会碰到这几个东西,因此咱们稍微简单提了一下。题外话到此结束,咱们如今进入正题。webpack

resolve配置

resolve.extensions

咱们在编辑器上开发项目代码,但这个傻怂编辑器IDE功能并非很强,每次import进来的文件都没有后缀名。若是你使用的是脚手架工具,你可能会发现一个有趣的地方,就是当咱们引入js、jsx或者vue文件的时候,咱们不须要加后缀就可使用了。可是你写了一些less或者sass引入到文件中,也没有添加后缀,编译直接报错。这个其实就是resolve的问题。es6

resolve: {
    // Add '.ts' and '.tsx' as resolvable extensions.
    extensions: [".ts", ".tsx", ".js", ".json"]
}复制代码

对resolve进行配置能设置模块如何被解析。
这里的extensions就是后缀的使用,我这里默认是ts/tsx/js/json,这些文件在import时,不添加后缀是能够的,只须要在数组中添加你想要省略的后缀名就能够达到一样的效果。好比下面这种web

extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx', '.less']复制代码

如今就算编辑器不给你添加后缀,你也不须要再加上后缀了,是否是省了不少事。

resolve.alias

这里是建立 import 或 require 的别名,来确保模块引入变得更简单

resolve: {
    alias: {
      Utilities: path.resolve(__dirname, 'src/utilities/'),
      Temp: path.resolve(__dirname, 'src/templates/')
    }
}复制代码

以前你引入src/template里面的1文件多是这样

import 1 from '../src/template/1';复制代码

如今你能够这样写了

import 1 from 'Temp/1';复制代码

这里的路径仍是具体看你的文件路径。不要照抄照搬哦。
这两项是我我的以为使用频率比较多的,其余状况请去官方文档查看下。

全局变量的使用

使用全局变量进行url的替换

如今项目仍是在开发阶段,你可能经过下面的接口获取信息

http://www.xxx.com/test/v3 + 具体接口复制代码

这个接口是放在测试服务器上的,但项目一旦上线要使用线上服务器

http://www.xxx.com/api/v4 + 具体接口复制代码

你因而冒出了一个很傻X的想法,本地开发或者测试时使用上面的接口,等到上线的时候再将它改掉。鬼鬼,咱不能这么秀。我给你提供一个好方法。

使用DefinePlugin插件来建立全局变量来解决这个问题

DefinePlugin容许你建立一个在编译时能够配置的全局常量,咱们下面建立一个名为url的全局变量,若是你是将开发和生产环境的webpack配置文件分开,你能够这样写

开发环境
plugins: [
    new webpack.DefinePlugin({
        url: JSON.stringify('http://www.xxx.com/test/v3')
    })
]复制代码
生产环境
plugins: [
    new webpack.DefinePlugin({
        url: JSON.stringify('http://www.xxx.com/api/v4')
    })
]复制代码

若是你只有一个webpack配置文件,你也能够写成这样

plugins: [
    new webpack.DefinePlugin({
        url: process.env.NODE_ENV === 'production' ?
            JSON.stringify('http://www.xxx.com/api/v4') :
            JSON.stringify('http://www.xxx.com/test/v3')
    })
    // 这里其实涉及到一个问题,就是你在生产环境的时候必须增长命令修改process.
    // env.NODE_ENV = production,不然上面的代码是不生效的
    // 在package.json的scripts对象中可使用,使用方法见我上一篇webpack的文章
]复制代码

因为这个变量必须包含字符串引号,因此你要么使用'"你的变量内容"', 或者使用 JSON.stringify('你的变量内容')这种形式。
如今你在项目中的接口url就能够写成这样了

`${url}/接口信息` // es6的字符串模板应该都懂吧?复制代码

你console.log(url)也是能够的哦,如今咱们就解决了这个恶心的问题。
可是还有更恶心的问题等着咱们,哈哈。真滴烦!!!
若是你的项目是脚手架搭建,每每会有eslint,eslint不进行设置是没办法使用这个全局变量的,找到eslint配置文件,添加以下代码

"globals": {
    "go": true
}复制代码

多入口文件打包

为何要设置多文件入口打包

在不少状况下,咱们都是用当下流行的框架进行web开发,好比vue、react。在开发一段时间事后,测试ok,咱们准备build项目了,可是打包以后文件是4.5MB,这玩意对pc或者移动来讲都不是一个很好的体验。若是咱们不去管它,那每次咱们改版或其余的一些状况,用户都须要去从新加载4.5mb的文件,哪怕你只是修改了一行代码。

多入口实现第三方库文件的单独打包

使用多入口文件配合插件解决此问题

entry: {
    vendor: ['react', 'react-dom'],
    app: "./src/index.tsx"
}复制代码

这里咱们设置了两个入口,一个是app,就是咱们传统使用的入口文件。而vendor使用的是一个数组,咱们把react和react-dom单独提取出来进行打包。这些库咱们基本上是不会改动源代码的,若是咱们把它们单独打包出来,即使咱们修改了项目的代码,react和react-dom的代码都不须要变,这时浏览器都直接读取vendor文件的缓存就能够了,减小了每次加载资源的体积,加强了用户体验。

配合插件CommonsChunkPlugin使用

只是增长入口文件是无论用的,咱们须要使用插件把vendor文件从app文件中剥离出来

plugins: [
    new webpack.optimize.CommonsChunkPlugin('vendor')
]复制代码

如今咱们已经把vendor和app文件分割了。这里只是举了一个简单的栗子,小伙伴能够根据本身的需求本身进行配置。

咱们也能够把公共组件进行一个单独的打包,这里再也不赘述,感兴趣的小伙伴能够本身试验哦。

html-webpack-plugin

以前的文章其实已经介绍过这个插件了,但此次咱们稍微具体的说一下。咱们使用脚手架生成的index.html其实你是找不到script标签引入js文件的,可能你也想这样作,本身动手引入真的麻烦。html-webpack-plugin来帮你

plugins: [
    // Generates an `index.html` file with the <script> injected.
    new HtmlWebpackPlugin({
      inject: true, // 这个配置项为true表示自动把打包出来的文件经过自动生成script标签添加到html中
      template: index.html, // 模板文件,其实若是没有特殊要求,能够考虑就是用本来的html文件,再也不单首创建模板。
      minify: { // 压缩的配置,感兴趣的同窗意义本身查下意思
        removeComments: true,
        collapseWhitespace: true,
        removeRedundantAttributes: true,
        useShortDoctype: true,
        removeEmptyAttributes: true,
        removeStyleLinkTypeAttributes: true,
        keepClosingSlash: true,
        minifyJS: true,
        minifyCSS: true,
        minifyURLs: true,
      },
    })
]复制代码

你的模板html文件是这样

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>Hello React!</title>
</head>
<body>
<div id="example"></div>

<!-- Main -->
</body>
</html>复制代码

经过使用上面的html-webpack-plugin配置它就变成了这样

<!DOCTYPE html><html><head><meta charset="UTF-8"/><title>Hello React!</title></head><body><div id="example"></div><script type="text/javascript" src="vendor.19786c9df38012fdca96.js"></script><script type="text/javascript" src="app.19786c9df38012fdca96.js"></script></body></html>复制代码

这样其实就是和你用脚手架搭建的一毛同样了。

讲一些项目中会出现的问题--devServer的使用

咱们在使用webpack时,经常会用到webpack-dev-server。它给咱们提供了一个server,使咱们的项目能跑server上。

historyApiFallback

如今项目在本地正常运行,你看了下地址栏

localhost:8080/#复制代码

leader说#是什么鬼?必须去掉,你见过谁的网址带#?
这个#实际上是hash路由进行路由跳转的依托。它是能够去掉的。react4是使用BroswerRouter替换HashRouter便可,vue的话同窗去查一下便可。去掉#以后咱们使用的就是h5的history模式进行路由跳转了。

你觉得这样就好了么?

可是老板又有新要求了,咱们的页面不是放在根目录下。网址是这样www.xxx.com/xxx,这个时候你要想页面放在服务器上能正常使用就须要在index.html添加base标签。
你信心满满的进行试验,发现,我擦,报错了,连页面都找不到了。


这个缘由是由于如今在这个路径下咱们是找不到资源文件,会报404的问题,这个时候咱们必需要设置

devServer: {
    historyApiFallback: true
  }复制代码

这个东西就是告诉webpack-dev-server,再找不到文件的时候默认指向index.html,底层的东西我并无去深刻查询,感兴趣的同窗能够去查下。

有些状况下,咱们可能会起不止一个服务,这个时候端口每每就会冲突,添加port属性,修改下端口便可解决冲突问题。

devServer: {
    historyApiFallback: true,
    port: 1234
  }复制代码

如今,地址就变成了localhost: 1234了。
devServer其实还有不少配置项,感兴趣的同窗能够去官网查看

webpack的热替换问题

热替换俗称HMR,即只更新你修改的局部内容,而不刷新整个页面,大大提升开发效率。这个只能在开发时使用哦,下面简单说一下用法。后面直接说react和vue的使用。

const webpack = require('webpack');

  module.exports = {
    entry: {
      app: './src/index.js'
    },
    devServer: {
     hot: true
    },
    plugins: [
     new webpack.HotModuleReplacementPlugin()
    ]复制代码

下面是index.js文件须要的配置(这里以为比较鸡肋,由于若是你不把js全写在一块儿,你就要每一个文件都要添加这个东东。)

+ if (module.hot) {
+   module.hot.accept('./app.js', function() {
+     console.log('Accepting the updated printMe module!');
+     printMe();
+   })
+ }复制代码

module.hot.accept 接受两个参数,第一个参数是修改的文件,第二个是会掉函数。这里只要app.js修改,就会触发回调。

咱们大多数状况下是使用react或者vue的,在这种状况下,你使用webpack的HMR是不起做用的。由于它没法保存这些框架的状态。
react和vue给咱们提供了解决方案

  • react-hot-loader
  • vue-loader

    同窗们能够在npm中搜索这两个东西的用法,都有对应的使用。

此部分webpack配置要去npm找到对应的loader看,很简单。我主要讲我当时很困惑的一点。
我主要讲一下react的,就是在使用路由和redux的状况下,咱们的index.js文件应该怎么写。

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import { AppContainer} from 'react-hot-loader'
import registerServiceWorker from './registerServiceWorker';
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import reducers from "./reducer";
import "./index.css";
import App from "./App";
import './style/style';

// redux的配置,能够忽略
const store = createStore(reducers, compose(
    applyMiddleware(thunk),
    window.devToolsExtension ? window.devToolsExtension() : ()=>{}
));

registerServiceWorker();

使用AppContainer包裹根组件便可

const render = Component => {
  ReactDOM.render(
      <AppContainer>
          <Component store={store}/> 
      </AppContainer>,
      document.getElementById('root')
  )
};
render(App);
// App做为内容的根组件,redux和router所有放在里面,只要有内容修改,就调用render函数
if (module.hot) {
    module.hot.accept('./App', () => { render(App) })
}复制代码

App文件

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import { BrowserRouter, Switch, Route } from 'react-router-dom';
import { Provider } from 'react-redux';

import Second from "./second";
import Third from "./Third";
import Header from "./header";
import First from "./first";
class App extends Component {
  render() {
    return (
      <Provider store={this.props.store}>
          <BrowserRouter className="App">
              <div>
                  <Header />
                  <Switch>
                      <Route exact path="/" component={First}/>
                      <Route path="/second" component={Second}/>
                      <Route path="/third" component={Third}/>
                  </Switch>
              </div>
          </BrowserRouter>
      </Provider>
    );
  }
}

export default App;复制代码

我告你,如今你开发,根本不须要页面刷新,超爽的。修改哪里,哪里变化。谢谢你们

相关文章
相关标签/搜索