webpack 学习笔记1(入门)
由于笔者的nodejs版本比较新,所以用webpack4会有一些bug。因此直接选择了webpack5
版本: node -v #v18.15.0
#1 安装
采用本地安装,没有全局安装。因为不同的项目可能使用的webpack版本不同,所以全局安装会有一些问题。还是一个项目一个版本比较好。
#由于本地无通过webpack -v ;webpack-cli -v 查看版本,不过可以直接在package.json里看到版本
npm install -D webpack #@5.78.0
npm install -D webpack-cli #@5.0.1
#2 一个极其简单的例子
先写一个webpack.config.js文件,将login模块打包到build中
const path = require('path'); module.exports = { entry: { "login":"./data/login.js"}, output: { filename: '[name].js', path: path.resolve(__dirname, 'build2'), clean: true//自动删除之前的文件 } };
然后写login代码(以及调用到的js的代码)
//login.js const name = require("./name") console.log("abc_log:",name);
//name.js module.exports="jack";
使用npm init初始化package.json文件,过程中选择webpack.config.js文件作为配置文件。
注意在script中需要添加"build":"webpack xxxxxxx",因为webpack非全局安装,所以cmd中webpack命令无法直接使用。通过此方法然后调用 npm run build 就可以运行webpack了。
如果不使用scripts的形式。直接打包使用命令 npx webpack --mode=development
{ "name": "html", "version": "1.0.0", "description": "", "main": "webpack.config.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build":"webpack --mode=development", "pro":"webpack --mode=production" }, "author": "", "license": "ISC", "devDependencies": { "webpack": "^5.78.0", "webpack-cli": "^5.0.1" } }
此时就生成了login.js的文件,如下(经过编码的,不过还是可以看到一些关键字):
(()=>{var o={341:o=>{o.exports="jack"}},r={};function t(e){var s=r[e];if(void 0!==s)return s.exports;var n=r[e]={exports:{}};return o[e](n,n.exports,t),n.exports}(()=>{const o=t(341);console.log("abc_log:",o)})()})();
至此一个最最简单的实例就完成了。大的框架由此开始编写!
#3. 用html-webpack-plugin,从模板html生成目标html
npm install -D html-webpack-plugin #笔者的版本是@5.5.0
然后新建一个tpl.html模板如下:
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no,maximum-scale=1" /> <!--常用库写这里,.........--> <!-- 这里的titile在config.js中设置--> <title><%= htmlWebpackPlugin.options.title %></title> </head> <body> <div id="content"></div> </body> </html>
webpack.config.js新增配置如下:
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装 const dist = "../../nginx-1.22.1/nginx-1.22.1/html/data"; module.exports = { entry:{ "investors_shares":"./data/investors_shares.js" }, output: { filename: '[name].[chunkhash].js',//模块名字+MD5 path: path.resolve(__dirname, dist), clean: true//自动删除之前的文件 }, plugins:[ new HtmlWebpackPlugin({ title: "XXX", filename: "investors_shares.html",//目标文件 template: "./data/tpl.html",//源文件 inject: "body",//插入到body元素后 minify:{ removeComments:true//移除HTML中的注释 }, chunks:["investors_shares","XXX"]//这里制定引入哪些js,如果不写,则默认引入html同名的 }), ] };
运行npm run build后,生成一个js和一个html文件。可以直接用浏览器查看输出
#4 安装react react-dom 以及babel相关
npm i -D react@15.4.2 react-dom@15.4.2 babel-loader @babel/core @babel/preset-env @babel/preset-react
笔者的最终安装版本如下:
"devDependencies": { "@babel/core": "^7.21.4", "@babel/preset-env": "^7.21.4", "@babel/preset-react": "^7.18.6", "babel-loader": "^9.1.2", "react": "^15.4.2", "react-dom": "^15.4.2", 。。。
这里略作解释(参考http://events.jianshu.io/p/822b8cc0fb4e):
babel-loader:使用Babel转换JavaScript依赖关系的Webpack加载器, 简单来讲就是webpack和babel中间层,允许webpack在遇到js文件时用bable来解析
@babel/core:即babel-core,将ES6代码转换为ES5。7.0之后,包名升级为@babel/core。@babel相当于一种官方标记,和以前大家随便起名形成区别。
@babel/preset-env:即babel-preset-env,根据您要支持的浏览器,决定使用哪些transformations / plugins 和 polyfills,例如为旧浏览器提供现代浏览器的新特性。
@babel/preset-react:即 babel-preset-react,针对所有React插件的Babel预设,例如将JSX转换为函数.
编写webpack.config.js如下,新增了rules部分,如下:
XXX output: { XXX }, module: { rules: [ { test: /\.js|jsx$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: [ ['@babel/preset-env', { targets: "defaults" }], ['@babel/preset-react'] ] } } } ] }, plugins:[ XXX
js部分如下随便添加一些代码:
let React = require('react'); let ReactDOM = require('react-dom'); let gf = require("./gf") class Index extends React.Component{ constructor(props) { super(props); this.state = {}; } componentWillMount() { } componentDidMount() { } render () { return( <div>{gf.DOMAIN+"javamy"}</div> ); } } ReactDOM.render( <Index />, document.getElementById('content') );
gf.js如下
module.exports = { fun:(name)=>{ return jack; }, DOMAIN : '//' + location.host + '/api' }
运行npm run build,就生成了一个很大的XX.js文件。此时这个文件是将react,jquery等代码的代码都组装到里面了,具体没研究。后续肯定要分离开,不然没法复用公共库了。
# 5如何在webpack中使用jquery
最简单的方法就是在html中将jquery的文件引入进来,然后直接调用即可。本文这里所想实现的(或者说学习的)是将jquery进入到工程模块,一并打包。
这个时候就需要首先安装jquery
npm install --save jquery
#这里其实用--save-dev也可以,因为只要improt了并使用了的函数或者库,不管他是devDependencies或者dependencies里都会进行打包处理。具体参看:https://zhuanlan.zhihu.com/p/215887185
#这种与nodejs做后端用的express框架那种不一样。
然后在js代码中引入jquery
import $ from 'jquery'; //或者使用 var $ = require("jquery"); $.ajax({ url: gf.DOMAIN+'api/getuserinfo', type: 'GET', xhrFields: {withCredentials: true}, cache:false, data: {}, 。。。。。
这是时候用npm run build会发现最终的js包变大了很多,就是把jquery代码也写进去了。当然也可以将公共库分离出来避免多个页面的js多次重复打包
#在webpack5中使用css sass scss
npm install --save-dev style-loader css-loader sass-loader sass #笔者版本为css-loader@6.7.3 sass-loader@13.2.2 style-loader@3.3.2 sass@1.62.0
css-loader: 把 css 文件编译为 CommonJS 模块,并将该模块引入到 JS 中。
style-loader:创建 <style>
标签,将 JS 中的 CSS 添加到 HTML中。
sass-loader、sass:不解释。
webpack.config.js中rules中增加如下:
module: { rules: [ ... { test: /\.s?[ca]ss$/, //sass 或者scss css use: [//加载顺序是从后先做,即先sass-loader,再css-loader,最后style-loader。 'style-loader',//style-loader是将css放置到页面上 'css-loader', 'sass-loader', ] } ] },
新建一个app.scss文件
.app-frame { margin: auto; width: 100%; max-width: 1000px; }
app.js中引入文件
import './app.scss' 。。。 。。。 render () { return( <div className='app-frame'></div> ); } } ReactDOM.render( <Index />, document.getElementById('content')
npm run build后,刷新页面直接生效
#在webpack5中使用BootStrap
参考https://getbootstrap.com/docs/4.0/getting-started/webpack/
npm install --save bootstrap@4.6.2 jquery popper.js #注意popper后面必须有.js,原因不知。popper的版本为1.16.1。
#使用bootstrap必须安装popper.js和jquery。不过笔者经过实验,对于bootstrap4.6.2版本,其安装的时候,会将popper.js和jquery默认进行安装。所以其实只需要install --save bootstrap@4.6.2即可
根据 https://v4.bootcss.com/docs/getting-started/introduction/ 可知,一般bootstrap有2中调用依赖关系,本节使用的是第二种,即插件独立:
<!-- Bootstrap 的 CSS 文件 --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous"> 。。。 <!-- JavaScript 文件是可选的。从以下两种建议中选择一个即可! --> <!-- 选项 1:jQuery 和 Bootstrap 集成包(集成了 Popper) --> <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-Fy6S3B9q64WdZWQUiU+q4/2Lc9npb8tCaSX9FK7E8HnRr0Jz8D6OP9dO5Vg3Q9ct" crossorigin="anonymous"></script> <!-- 选项 2:Popper 和 Bootstrap 的 JS 插件各自独立 --> <!-- <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.min.js" integrity="sha384-+sLIOodYLS7CIrQpBjl+C7nPvqq+FbNUBDunl/OZv93DB7Ln/533i8e/mZXLi/P+" crossorigin="anonymous"></script> --> </body>
js代码中引入bootstrap,根据文档(和实际实验)popper.js和jquery不需要手动引入
//import 'popper.js'
//import 'jquery' import 'bootstrap'
app.scss中直接引入bootstrap的s?[ca]ss文件如下:(当然在app.js中直接import也行)
//app.scss 。。。。$lightGrayColor:#eee; @import '~bootstrap/scss/bootstrap'; .app-frame { ma。。。。
#devtool的配置
Webpack里有个配置项叫做devtool,这个配置项主要是用于配置打包后的代码映射,方便我们在debug的时候查看到源代码的位置,主要是发生异常错误的时候,可以定位到准确位置。
output: { XXX }, module: { rules: [ { } ] }, devtool: 'inline-source-map',//如果是生产环境,用'source-map' plugins:[ XXX
在生产环境,一般使用'source-map',此时js的包会比没有devtool的时候大一些,而且多了要给*.map文件。浏览器就是通过这个map定位到错误。如果是在生产环境使用'inline-source-map',操作上也可以,但是那么js文件会很大,所以极其不推荐。
在开发环境,按照官方推荐,一般使用'inline-source-map'。虽然有source-map,但是也有inline字段,所以这个时候,也是生产一个map文件内容对应的base64插入到*.js里面去。操作上,也可以使用'source-map',只是一边使用'inline-source-map'。
#文件引入file-loader(图片,附件等)
file-loader可以协助在webpack中引入图片等。
npm install --save-dev file-loader //@6.2.0
在配置文件中
module: { rules: [ { test: /\.js|jsx$/, XXX }, { test: /\.(png|svg|jpg|ico|gif|bmp|pdf|jpe?g)$/, use: [ { loader:'file-loader', options:{ name:'[name].[ext]', outputPath:'img'//会输出到dist下的img文件夹 } } ] }, ] }, plugins:[
js文件中的代码这样写:
import logo from './img/cil-notes.svg'; <img height='100%' src={logo} />
注:与file-loader对应常用的loader是已下3个,本文只涉及到file-loader:
raw-loader 将文件导入为字符串
url-loader 将文件作为 data URI 内联到 bundle 中
file-loader 将文件发送到输出目录
然而在webpack5开始,不建议使用以上3个了,而改用以下的形式【https://www.webpackjs.com/guides/asset-modules/#root】:
资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader:
asset/resource
发送一个单独的文件并导出 URL。之前通过使用file-loader
实现。asset/inline
导出一个资源的 data URI。之前通过使用url-loader
实现。asset/source
导出资源的源代码。之前通过使用raw-loader
实现。asset
在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用url-loader
,并且配置资源体积限制实现。
当在 webpack 5 中使用旧的 assets loader(如 file-loader
/url-loader
/raw-loader
等)和 asset 模块时,你可能想停止当前 asset 模块的处理,并再次启动处理,这可能会导致 asset 重复,你可以通过将 asset 模块的类型设置为 'javascript/auto'
来解决。
具体用法参考下一篇文章:webpack 学习笔记2(进阶)
XXX
XX