react with webpack4

Let me preface this section by saying that webpack is hard and will be confusing if this is your first experience with a code bundler. If you have experience with other tools like Gulp or Grunt it's a bit easier, but if you don't, just hold on and it will get easier. We're going to hyper-focus on the real meat of webpack for this course and jump into more advanced features in other courses.

The first questions you should ask yourself whenever you're using a new tool are "Why does this thing exist?" and "What problem is this thing solving?". If you can't eventually answer those two questions, you probably don't need it. So let's answer those questions in terms of webpack.

为什么存在
Why does webpack exist? webpack, at its core, is a code bundler. It takes your code, transforms and bundles it, then returns a brand new version of your code.

解决了什么问题
What problem is this thing solving?
Think about how many times we have to take our code and change it so it's compliant with what the browser is used to (vanilla HTML, CSS, and JavaScript). If you've ever used a CSS Preprocessor like SASS or LESS you know you need to transform your SASS/LESS code into normal CSS. If you've ever used CoffeeScript or any compile to JavaScript language you know there's also a conversion step there. So where webpack really shines is you're able to tell it every transformation your code needs to make, and it will do them and output a bundle file for you full of those changes (and some other helpful things as well like minification if you desire).

怎么做
If you think about the process we talked about above, that idea of taking your code and transforming it in someway then spitting it out - there are really three main steps and three main things webpack needs to know.

入口

  1. webpack needs to know the starting point of your application, or your root JavaScript file.

如何转化

  1. webpack needs to know which transformations to make on your code.

出口

  1. webpack needs to know to which location it should save the new transformed code.

The code for this might look a little scary at first, but know that all we're doing is coding out those three steps above so webpack can understand them.

Reminder: Text sections are just supposed to be read, not coded along with. So absorb as much as you can then in the video we'll walk through creating our webpack.config.js file together.

The first thing we need to do is create a file which is going to contain our webpack configurations. Conveniently, this file should be named webpack.config.js and be located in the root directory of our project.

Now that we have our file made we need to make sure that this file exports an object which is going to represent our configurations for webpack. If you're not familiar with JavaScript modules, this blog series is great.

// In webpack.config.js 
module.exports =  {}

Now let's go ahead and walk down our list of those three items and convert them into code. First up, telling webpack where the entry point of our application is located. This one is pretty straight forward and looks like this,

// In webpack.config.js
module.exports = {
  entry: './app/index.js',
}

All we do is give our object a property of entry and a value which is a string which points to our root JavaScript file in our app.

Now that we've told webpack where to start, we need to tell it which transformations to actually make. This is where loaders will come in handy.

Let's say that for some reason we're still writing CoffeeScript in 2017 (ZING!). We would need a way to transform our CoffeeScript into regular JS. Sounds like the perfect place for a CoffeeScript loader. First step, we need to install the loader we want. We'll use npm to do this. In your terminal you would run npm install --save-dev coffee-loader which would then save coffee-loader as a dev dependency in your package.json file. Next, we need to configure our webpack.config.js file to be aware of this specific transform. To do that we'll first need to add a module property to the object we're exporting in webpack.config.js and then that module property will have a property of rules property which is an array.

// In webpack.config.js
// In webpack.config.js
module.exports = {
  entry: [
    './app/index.js'
  ],
  module: {
    rules: []
  },
}

Inside of that rules array is where we're going to put all of our different loaders or transformations we want to take place.

Each loader needs to be composed of two things. The first is which file type to run the specific transformation on. For example, we don't want to run CSS transformations on a JavaScript file and vice versa. The second thing is the specific loader we want to run. Let's take a look at what this looks like.

// In webpack.config.js
module.exports =  { 
  entry:  
    './app/index.js',
     module:  { rules:  [  { test:  /\.coffee$/, use:  "coffee-loader"  }  ]  
      }, 
 }

The first thing you'll probably notice is the test:/.coffee$/ section. It looks scary if you're not used to regular expressions but all it's doing is it tells webpack to run the coffee-loader on all extensions that end in .coffee.

Next step, use tells webpack which transformation to run on all paths that match the test RegEx.

As you can see, the steps for including more loaders is pretty basic. NPM install the specific loader then add a new object to the rules array.

Now that we've done steps 1 and 2, we just have one more step. This last step is specifying where webpack should output the new transformed code.

// In webpack.config.js
module.exports = {
  entry: './app/index.js',
  module: {
    rules: [
      { test: /\.coffee$/, use: "coffee-loader" }
    ]
  },
}

Again the code here is pretty self explanatory. filename is the name of the file that webpack is going to create which contains our new transformed code. path is the specific directory where the new filename (index_bundle.js) is going to be placed. If you've never seen __dirname before that's just referencing the name of the directory that the currently executing script resides in.

So now when webpack runs, our code will be transformed and then can be referenced at ourApp/dist/index_bundle.js. Well that's great, but now we need to come up with a plan to get our HTML to reference this specific file. There are a few options, but most are crappy. If we look at how normal apps are usually structured it's usually something like this.

/app
  - components
  - utils
  index.js
  index.html
/dist
  index.html
  index_bundle.js
package.json
webpack.config.js
.gitignore

So as you can see, our code we're developing with is found in the app folder and our transformed code is in the dist folder. Now you can visually see the issue. We want to change the index.html located in the app folder but the index.html file that the browser is actually going to be using is located in the dist folder (because that's where we've also told webpack to spit out the transformed JS file).

The first option to solve this is to just manage two index.html files and whenever you change the one located in /app, copy/paste that to the one located in /dist. Though this will work, I won't be able to look my Wife in the eyes again if we do this.

Second option, we could figure out a way so that whenever webpack runs, our /app/index.html gets copied over to /dist/index.html. This sounds a lot better and the final solution will look close to this.

As you can probably guess, there's already a Webpack tool that allows us to do something similar. Instead of actually copying our index.html file, it's just going to use that file as a template and create a brand new index.html file. This plugin is the html-webpack-plugin. As always, you'll need to run npm install --save-dev html-webpack-plugin before you can use it. Now we just need to tell webpack what we want to do with it.

First thing, we'll need to create a new instance of HTMLWebpackPlugin and specify one thing, the template of what we want the newly created file to look like.

Let's see what all the code looks like.

// In webpack.config.js
module.exports = {
  entry: './app/index.js',
  module: {
    rules: [
      { test: /\.coffee$/, use: "coffee-loader" }
    ]
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'index_bundle.js'
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'app/index.html'
    })
  ]
}

You'll notice we've added a plugins property on our object and the first item in the array is a new instance of HtmlWebpackPlugin with template pointing to our regular index.html file located in our app directory.

Now if we run webpack from our command line, inside of our dist folder we'll have two files. index_bundle.js and index.html. index_bundle.js is the result of taking our entry code and running it through our loaders. While index.html was created on the fly with HTMLWebpackPluginConfig and is a copy of our original index.html file located in our app folder with a script tag referencing the newly created index_bundle.js file

Let's take a look at what our index.html file inside of our app folder looks like then what our newly created index.html file inside of dist looks like.

app/index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>My App</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <div id="app"></div>
</body>
</html>

dist/index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>My App</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <div id="app"></div>
  <script src="index_bundle.js"></script>
</body>
</html>

You'll notice that the only difference between the two files is that the one in dist (which was created with HTMLWebpackPlugin) now has a script tag pointing to index_bundle.js . Again, the only real magic going on here is that HTMLWebpackConfig is smart enough to detect the output filename of your newly created file from Webpack and it will automatically add that as a script in your newly created index.html file. So in our example we used index_bundle.js as the output filename so as you can see in the created index.html file above, we have now have <script src="index_bundle.js"></script> inside the body. If we were to change the output of our webpack config to be OUR-AWESOME-JS-FILE.js, then inside the body of our newly create index.html file we would have <script src="OUR-AWESOME-JS-FILE.js"></script>

Finally, as of Webpack 4, back in our webpack.config.js file we need to tell it which "mode" we want it to run in - "production" or "development". For now, we'll just set the mode to "development". This will enable things like tooling for debugging and faster builds.

// In webpack.config.js
module.exports = {
  entry: './app/index.js',
  module: {
    rules: [
      { test: /\.coffee$/, use: "coffee-loader" }
    ]
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'index_bundle.js'
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'app/index.html'
    })
  ],
  mode: "development"
}

Now the only other crucial piece of information is how to actually tell webpack to run.

In order to do that, you'll need to install two things. "webpack" and "webpack-cli". Once installed, you'll be able to add a new script to your package.json which runs webpack.

package.json

"scripts": {
  "build": "webpack"
},

Now in your terminal you can run "npm run build" which will do a one time run through of your settings then compile your code and output into a dist folder. However, this can be kind of a pain to keep having to run the command over and over whenever you change anything. To fix this, change webpack in your NPM script to run webpack -w and that will watch your files and re-execute webpack whenever any of the files Webpack is concerned about changes. Lastly, if you're wanting to ship to production, you can run webpack -p and that will run through the normal transformations as well as minify your code (we'll do this in the last video of the course).

So now you're probably thinking, "Hey Tyler. That's cool and all, but I came here for React and I still haven't seen any of it." First off, calm down. Second, that's fair. The good news is you already have all the webpack knowledge you need to get going with React. Now we just need to look at a tool called babel and add it as a loader.

Babel.js is a wonderful tool for compiling your JavaScript. With Webpack you tell it which transformations to make on your code, while Babel is the specific transformation itself. In terms of React, Babel is going to allow us to transform our JSX (which you'll see soon) into actual JavaScript. What's nice about Babel though is it can be used for much more than just JSX -> JS transformations. You can also opt into "future" versions of JavaScript (ES2015, 2016, etc) and use Babel to transform your future JavaScript to modern day JavaScript so the browser can understand it. The overview of implementing Babel is that we're going to npm install a bunch of tools specific to Babel, then we'll add babel as a loader in our webpack settings. Then whenever webpack bundles our code, it'll transform our code with whatever we specified with Babel. You'll see the specific details of this in the next video.

That's it! I realize that's a lot of steps but you've probably realized that the code for these steps isn't actually difficult, it's just piecing everything together that is.

Webpack 4 设置

yarn init
yarn add react react-dom
yarn add babel-core babel-loader babel-preset-env babel-preset-react css-loader style-loader html-webpack-plugin webpack webpack-dev-server webpack-cli --dev
// package.json 加上
  "babel": {
    "presets": ["env", "react"]
  },
// 一个 package.json 的例子

{
  "name": "try",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "babel": {
    "presets": ["env", "react"]
  },
  "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-loader": "^7.1.4",
    "babel-preset-env": "^1.7.0",
    "babel-preset-react": "^6.24.1",
    "css-loader": "^0.28.11",
    "html-webpack-plugin": "^3.2.0",
    "style-loader": "^0.21.0",
    "webpack": "^4.8.3",
    "webpack-cli": "^2.1.3",
    "webpack-dev-server": "^3.1.4"
  },
  "scripts": {
    "start": "webpack --open",
    "dev": "webpack-dev-server --open --hot"
  },
  "dependencies": {
    "react": "^16.3.2",
    "react-dom": "^16.3.2"
  }
}
// you need to tell Webpack which "mode" you want to run in: "production" or "development".
// 入口创建一个 app 文件夹, 建立 index.js 的文件作为入口文件
//  In webpack.config.js
var path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: './app/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'index_bundle.js'
  },
  module: {
    rules: [
      { test: /\.(js)$/, use: 'babel-loader' },
      { test: /\.css$/, use: [ 'style-loader', 'css-loader' ]}
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'app/index.html'
    })
  ],
  mode: "development"
};
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,002评论 6 509
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,777评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,341评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,085评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,110评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,868评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,528评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,422评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,938评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,067评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,199评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,877评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,540评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,079评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,192评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,514评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,190评论 2 357

推荐阅读更多精彩内容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi阅读 7,338评论 0 10
  • 《红楼梦》开篇是女娲补天故事。元气十足。曹雪芹压缩了全部的历史,把自己的架构直通远古——元气时代。女娲补天、精卫填...
    狮肩瘦阅读 784评论 0 1
  • 近一段时间迷上了青花釉里红瓷器,青花是以氧化钴作为釉药,烧成较釉里红容易,而釉里红则是以氧化铜为釉药,还原烧为氧化...
    康馨大帝阅读 418评论 0 1