0
点赞
收藏
分享

微信扫一扫

webpack通俗易懂(四)

墨春 2022-01-26 阅读 160

webpack通俗易懂(四)

  • 1,source-map
  • 2,devServer
  • 3, https
  • 4,
  • 5,

辛苦编写良久,还望点赞鼓励呦~

1,提高开发效率,完善团队开发规范

1.1,source-map

之前我们通过webpack, 将我们的源码打包成了 bundle.js, 实际上客户端(浏览器)读取的是打包后的 bundle.js, 当浏览器执行代码报错的时候,报错的信息自然也是 bundle 的内容。我们如何将报错信息(bundle错误的语句及其所在行列)映射到源码上,这时候我们就需要用 source-map了,webpack已经内置了sourcemap功能,我们只需要通过简单的配置,就可以开启它

module.exports = {
  // 开启 source map
  // 开发中推荐使用 'source-map' // 生产环境一般不开启 sourcemap 
  devtool: 'source-map'
}

当我们执行打包命令后,我们发现bundle的最后一行总是会多出一个注释,指向打包出的bundle.map.js(sourcemap文件)。 sourcemap文件用来描述源码文件和 bundle 文件的代码位置映射关系。基于它,我们将bundle文件的错误信息映射到源码文件上。
除开’source-map’外,还可以基于我们的需求设置其他值,webpack——devtool一共提供了7种SourceMap模式:

  • eval:每个module会封装到 eval 里包裹起来执行,并且会在末尾追加注释 //@ sourceURL.
  • source-map:生成一个SourceMap文件
  • hidden-source-map:和 source-map一样,但不会在 bundle 末尾追加注释
  • inline-source-map:生成一个 DataUrl 形式的 SourceMap 文件.
  • eval-source-map:每个module会通过eval()来执行,并且生成一个DataUrl形式的 SourceMap
  • cheap-source-map:生成一个没有列信息(column-mappings)的SourceMaps文 件,不包含loader的 sourcemap(譬如 babel 的 sourcemap)
  • cheap-module-source-map:生成一个没有列信息(column-mappings)的SourceMaps文件,同时 loader 的 sourcemap 也被简化为只包含对应行的。

要注意的是,生产环境我们一般不会开启sourcemap功能,主要有两点原因:

  1. 通过bundle和sourcemap文件,可以反编译出源码————也就是说,线上产物有soucemap文件的话,就意味着有暴漏源码的风险。
  2. 我们可以观察到,sourcemap文件的体积相对比较巨大,这跟我们生产环境的追求不同(生产环境追求更小更轻量的bundle)。

有时候我们期望能第一时间通过线上的错误信息,来追踪到源码位置,从而快速解决掉bug以减轻损失。但又不希望sourcemap文件报漏在生产环境,有什么比较好的方案呢?

1.2,devServer

开发环境下,我们往往需要启动一个web服务,方便我们模拟一个用户从浏览器中访问我们的web服务,读取我们的打包产物,以观测我们的代码在客户端的表现。webpack内置了这样的功能,我们只需要简单的配置就可以开启它。

安装 devServer

yarn add -D webpack-dev-server

devServer.proxy基于强大的中间件 http-proxy-middleware 实现的,因此它支持很多的配置项,我们基于此,可以做应对绝大多数开发场景的定制化配置。
基础使用:

const path = require('path')
devServer: {
  static: {
    directory: path.join(__dirname, 'dist')
  }, // 默认是把dist目录作为web服务的根目录
  compress: true, // 可选择开启gzips压缩功能,对应静态资源请求的响应头里的Content-Encoding: gzip
  port: 3000, // 端口号
},

为了方便,我们配置一下工程的脚本命令,在package.json的scripts里:

{
  "scripts": {
    "dev": "webpack serve --mode development"
  }
}
  • 1.2.1, 添加响应头

有些场景需求下,我们需要为所有响应添加headers, 来对资源的请求和响应打入标志,以便做一些安全防范,或者方便发生异常后做请求的链路追踪。比如:

module.exports = {
  devServer: {
    headers: {
      'X-Token': 'ZlcjLCe+sAW1S4QC8Z'
    }
  }
}

  • 1.2.2, 开启代理

我们打包出的js bundle里有时会含有一些对特定接口的网络请求(ajax/fetch). 要注意,此时客户端地址是在 http://localhost:3000/ 下,假设我们的接口来自 http://localhost:4001/,那制台就会报错跨域,在开发环境下,我们可以使用devServer自带的proxy功能来解决这个问题。

我们新搭建一个服务,在当前项目下新建 server.js:

const http = require('http');
const app = http.createServer((req, res) => {
  if (req.url === '/api/user') {
    res.end('hello node')
  }
})

app.listen(4001, 'localhost', () => {
  console.log('localhost listening on 4001')
})

再次打开一个终端执行node server.js,启动服务

浏览器输入:


下面我们开始请求,请求我们可以使用浏览器自带的方法fetch,这个方法返回的是一个promise

fetch('/api/user')
  .then(val => val.text()) // res.text()可以把返回的结果变成文本)
  .then(res => {
    console.log(res)
  })


如何解决上面跨域的问题呢:

module.exports = {
  //...
  devServer: {
    proxy: {
      '/api': 'http://localhost:4001'
    }
  } 
}
// 如果用户在地址栏一旦请求了一个资源叫 /api 的话,我们就给他指向到 http://localhost:4001 服务器上去

现在对 /api/user 的请求会将请求代理到 http://localhost:4001/api/user。如果不希望传递 /api, 则可以重写路径:
proxy: {
  '/api': {
    target: 'http://localhost:4001',
    pathRewrite: {
      '^/api': '/'  // 这里可以是'/'也可以是''
    }
  }
}

  • 1.2.3,https

默认情况下,将不接受在 HTTPS 上运行且证书无效的后端服务器。如果想让我们的本地http服务改为https服务,可以这样配置:

devServer: {
  https: true
}

重新启动服务:npx webpack, 我们发现访问http://localhost:port是无法访问我们的服务的,我们需要在地址栏里加前缀: https,注意: 由于默认配置使用的是自签名证书,所以有的浏览器会告诉你是不安全的,但我们依然可以继续访问它。当然我们也可以提供自己的证书:

module.exports = {
  devServer: {
    https: {
      cacert: './server.pem',
      pfx: './server.pfx',
      key: './server.key',
      cert: './server.crt',
      passphrase: 'webpack-dev-server',
      requestCert: true,
    }
  }
}

  • 1.2.4, http2

我们也可以不使用https,可以使用http2

如果想要配置http2,那么直接设置:

devServer: {
   http2: true
}

http2默认自带https自签名证书,当然我们仍然可以通过https配置项来使用自己的证书

  • 1.2.5, historyApiFallback

如果我们的应用是个SPA(单页面应用),当路由到/some 时(可以直接在地址栏里输入),会发现此时刷新页面后,控制台会报错:

GET http://localhost:3000/some 404 (Not Found)

此时打开network,刷新并查看,就会发现问题所在———浏览器把这个路由当作了静态资源的地址去请求,然而我们并没有打包出/some这样的资源,所以这个访问无疑是404的。如何解决它?我们可以通过配置来提供页面代替任何404的静态资源响应:

module.exports = {
  //...
  devServer: {
    historyApiFallback: true
  }
}

此时重启服务刷新后发现请求变成了index.html, 当然, 在多数业务场景下,我们需要根据不同的访问路径定制替代的页面,这种情况下,我们可以使用rewrites这个配置项。 类似这样:

module.exports = {
  //...
  devServer: {
    historyApiFallback: {
      rewrites: [
        { from: /^\/$/, to: '/views/landing.html' },
        { from: /^\/subpage/, to: '/views/subpage.html' },
        { from: /./, to: '/views/404.html' },
      ]
    }
  }
}
  • 1.2.6, 开发服务器主机

如果我们在开发环境中起了一个devserve服务,并希望在同一局域网下的同事也能访问它,只需要配置:

devServer: {
  host: '0.0.0.0'
}

这时候,如果我们的同事跟我们处在同一局域网下,就可以通过局域网ip来访问我们的服务 啦

1.3, 模块热替换与热加载

  • 模块热替换
    模块热替换(HMR - hot module replacement)功能会在应用程序运行过程中,
    替换、添加或删除 模块,而无需重新加载整个页面
module.exports = {
  //...
  devServer: {
    hot: true,
  },
}

HMR 加载样式,如果我们配置了style-loader,那么现在已经同样支持样式文件的
热替换功能了

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
      },
    ],
  },
}

这是因为style-loader的实现使用了module.hot.accept,在CSS依赖模块更新之后,
会对 style 标签打补丁。从而实现了这个功能。
热加载(文件更新时,自动刷新我们的服务和页面) 新版的webpack-dev-server
默认已经开启了热加载的功能。 它对应的参数是devServer.liveReload,默认为
true。 注意,如果想要关掉它,要将liveReload设置为false的同时,也要关掉
hot

module.exports = {
  //...
  devServer: {
    liveReload: false, //默认为true,即开启热更新功能。
  },
};

1.4,eslint

eslint是用来扫描我们所写的代码是否符合规范的工具。往往我们的项目是多人协作开发的,我们期望统一的代码规范,这时候可以让eslint来对我们进行约束。严格意义上来说,eslint配置跟webpack无关,但在工程化开发环境中,它往往是不可或缺的

yarn add eslint -D

配置eslint,只需要在根目录下添加一个 .eslintrc 文件(或者.eslintrc.json, .js等)。 当然,我们可以使用eslint工具来自动生成它:

npx eslint --init

我们可以看到控制台里的展示:


并生成了一个配置文件(.eslintrc.json),这样我们就完成了eslint的基本规则配置。 eslint配置文件里的配置项含义如下:

    1. env 指定脚本的运行环境。每种环境都有一组特定的预定义全局变量。此处使用的 browser 预定义了浏览器环境中的全局变量,es6 启用除了 modules 以外的所有 ECMAScript 6 特性(该选项会自动设置 ecmaVersion 解析器选项为 6)。
    1. globals 脚本在执行期间访问的额外的全局变量。也就是 env 中未预定义,但我
      们又需要使用的全局变量。
    1. extends 检测中使用的预定义的规则集合。
    1. rules 启用的规则及其各自的错误级别,会合并 extends 中的同名规则,定义冲
      突时优先级更高。
    1. parserOptions ESLint 允许你指定你想要支持的 JavaScript 语言选项。
      ecmaFeatures 是个对象,表示你想使用的额外的语言特性,这里 jsx 代表启用 JSX。ecmaVersion 用来指定支持的 ECMAScript 版本 。默认为 5,即仅支持 es5,你可以使用 6、7、8、9 或 10 来指定你想要使用的 ECMAScript 版本。你 也可以用使用年份命名的版本号指定为 2015(同 6),2016(同 7),或 2017(同 8)或 2018(同 9)或 2019 (same as 10)。上面的 env 中启用了 es6,自动设置了ecmaVersion 解析器选项为 6。 plugins plugins 是一个 npm 包,通常输出 eslint 内部未定义的规则实现。rules 和 extends 中定义的规则, 并不都在 eslint 内部中有实现。比如 extends 中的 plugin:react/recommended,其中定义了规则开关和等级,但是这些规则如何 生效的逻辑是在其对应的插件 ‘react’ 中实现的

修改配置文件:

{
  "rules": {
    "no-console": "warn" // 我们在rules里自定义我们的约束规范
  }
}

我们通过命令来让elisnt检测代码——在我们的package.scripts里添加一个脚本命令:


举报

相关推荐

0 条评论