여기서 ReactJS 와 함께 webpack 을 한 번 사용해 보자.
Webpack
이 webpack 은 모듈화를 위한 묶어주는 도구라고 보면 될 듯 하다. 보통 web page 는 html/js/css 로 이루어지는데, 원래 태생이 달라서 대부분 이녀석들을 하나로 묶어서 관리하지는 않았다.하지만 이제는 web application 이 좀 더 rich 해져서 이 녀석들을 하나로 묶어서 관리할 필요가 더욱 커지고 있다. 여하튼 이런 것들을 java 등에서는 언어자체에서 지원을 해주지만, web 진영에는 언어자체에서 이런 지원을 하는 것이 아니라서 여러가지 tool 들이 나와있다.
그 중에 어쩌면 요새 유행하는 녀석이라고 볼 수 있을 듯 하다. 여하튼 아직까지는 나도 낯설어서 설명이 허접할 것이니 감안하고 보자.
설치
npm init
D:\mine\programming\nodejs\js>npm init This utility will walk you through creating a package.json file. It only covers the most common items, and tries to guess sensible defaults. See `npm help json` for definitive documentation on these fields and exactly what they do. Use `npm install <pkg> --save` afterwards to install a package and save it as a dependency in the package.json file. Press ^C at any time to quit. name: (js) react-ecma6-boilerplate version: (1.0.0) 0.1.0 ...
npm install
d:\..> npm install --save react d:\..> npm install --save react-dom d:\..> npm install --save-dev webpack
webpack 에서 babel 을 사용하는 것은 babel-loader 를 통해 가능하다. loader 와 관련된 글을 읽어보자.
d:\..> npm install --save-dev babel-loader babel-core d:\..> npm install --save-dev babel-preset-es2015 babel-preset-react
tree shaking, babel-preset-es2015-native-modules
참고로 webpack 2 에서 tree shaking 을 제공하는데, 이것을 위해서는 "babel-preset-es2015-native-modules" 를 사용해야 한다.- babel-preset-es2015 --> babel-preset-es2015-native-modules
- Tree-shaking with webpack 2 and Babel 6
- webpack/examples/harmony-unused at master · webpack/webpack
- 이것이 babel-preset-es2015 안으로 들어갔다. 그래서 option 설정만 아래 처럼 바꿔주면 된다.
'es2015' --> ["es2015", { "loose": true, "modules": false }],
tree-shaking 을 통해 harmony export 가 결정되는데, 아래처럼 output 이 나오게 된다. 이 때는 export 를 선별해서 하고, export 를 하지 않는 module(unused harmony export)등도 같이 보여진다. 하지만, minimizing step 에서 사용하지 않는 부분을 없애준다.(--optimize-minimize 등)
확실히 필요하지 않은 부분을 없애주는지 확인하려했는데, 내 code 가 한 file 에 여러 component 를 가지고 있어서 그런지, 쓰지 않는 component 를 완전히 없애주지 못하는 듯 보였다. 필요한 부분만 정확히 묶고 싶은 경우라면, 각 component 를 각각 하나의 file 로 분류해야 할 듯 하다.
확실히 필요하지 않은 부분을 없애주는지 확인하려했는데, 내 code 가 한 file 에 여러 component 를 가지고 있어서 그런지, 쓰지 않는 component 를 완전히 없애주지 못하는 듯 보였다. 필요한 부분만 정확히 묶고 싶은 경우라면, 각 component 를 각각 하나의 file 로 분류해야 할 듯 하다.
"use strict"; /* harmony export */ exports["a"] = add;/* harmony export */ exports["b"] = multiply;/* unused harmony export list */function add() { var sum = 0, i = 0, args = arguments, l = args.length; while (i < l) { sum += args[i++]; } return sum; }
webpack.config.js
webpack.cmd 를 실행하려면 config 를 넣어줘야 한다.
var path = require('path'); var webpack = require('webpack'); var buildPath = 'build/' var commonsPlugin = new webpack.optimize.CommonsChunkPlugin(buildPath + 'common.js'); module.exports = { entry: { 'login': ['./src/js/login.jsx'], 'login2': ['./src/js/login2.jsx'], 'vendor' : ['react', 'react-dom'], }, output: { path: __dirname, filename: buildPath + 'app.bundle.[name].js' }, // Enable sourcemaps for debugging webpack's output. devtool: "source-map", resolve: { // Add '.ts' and '.tsx' as resolvable extensions. extensions: [".ts", ".tsx", ".js", ".json"], alias:{ // utils: path.join(__dirname, '/src/js/common/utils'), } }, module: { rules: [ // All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'. { test: /\.tsx?$/, loader: "awesome-typescript-loader" }, // All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'. { enforce: "pre", test: /\.js$/, loader: "source-map-loader" } ], loaders: [ { //tell webpack to use babel-loader for all *.jsx files test: /.jsx?$/, loader: 'babel-loader', exclude: /node_modules/, query: { presets: ['es2015', 'react'] } } ] }, // When importing a module whose path matches one of the following, just // assume a corresponding global variable exists and use that instead. // This is important because it allows us to avoid bundling all of our // dependencies, which allows browsers to cache those libraries between builds. externals: { "react": "React", "react-dom": "ReactDOM" }, plugins: [commonsPlugin] };
build
이제 webpack 을 이용한 build 를 해 보자. 아래처럼 command 를 사용하면 된다.
d:\..> node_modules\.bin\webpack.cmd
file-loader
require 와 연관된 녀석들만 copy 하는 듯 하다.
개인적으로 CopyWebpackPlugin 이 좀 더 직관적인 듯 하다. 그런데 build 가 끝나기전에 copy 를 하는 등의 문제가 있다.(참고: Copied file is empty · Issue #28)
d:\..> npm install --save-dev copy-webpack-plugin
css-loader
css-loader 를 사용하면 css 관리가 용이해 진다. 사용법은 아래를 참고하자.TypeScript + ReactJS
gulp
아직까지는 build 한 결과를 한 곳에 모아놓고, 이것을 dist 에 copy 하는 작업은 gulp 를 사용해야 할 듯 보인다.- npm install --save-dev gulp gulp-util
-
// gulpfile.js var gulp = require("gulp"); var gutil = require("gulp-util"); var path = require('path'); var webpack = require("webpack"); var config = require('./webpack.config.js'); gulp.task("webpack", function(callback) { // run webpack webpack(config).run(function(err, stats) { if(err) throw new gutil.PluginError("webpack", err); gutil.log("[webpack]", stats.toString({ // output options })); // copy to dist console.log('copy .js files to dist folder.') gulp.src([ './build/*.js' ]) .pipe(gulp.dest('../src/static/js')) callback(); }); });
- .\node_modules\.bin\gulp.cmd webpack
참고로, optimization option (--optimize-minimize, 등)을 사용했다면, 이녀석은 webpack.config.js 의 plugin 으로 설정해 주면 된다. 아래 문서를 참고하자.
CommonsChunkPlugin
common chucks 를 만들어주는 pluginlibrary 로 묶기
- library and externals
- webpack/examples/multi-part-library at master · webpack/webpack
- webpack/examples/externals at master · webpack/webpack
- krasimir/webpack-library-starter: Webpack based boilerplate for producing libraries (Input: ES6, Output: universal library)
webpack 을 이용하면, 기본적으로 자신들이 알아서 이름을 변경해서 사용한다. 그래서 이것을 그냥 <script> 로 include 해서 가져다 쓸 수 없다. 이를 위해서 webpack 의 configuration 에 external 을 설정해 주면 된다.
ref. 5 에 있는 source 로 설명을 하자면,
- externals 에 있는 부분은 webpack 으로 묶이지 않는다. library 중에 밖으로 노출시킬 녀석을 설정하면 되고,
- external 에 설정하는 것이 필요한 때는 library 를 bundle.js 로 묶는 경우에 library 중 하나라도 변경되면 bundle.js 가 변경돼서 browser 는 cache miss 를 발생시킬 것이다. 하지만 external 로 해놓으면 자신의 library 가 변경되어도 다른 cached library 에 영향을 주지 않게 된다.
- output.library 는 webpack 으로 만들어진 bundle.js 의 이름을 정해주는 것이라
보면 된다.
var jQuery = require("jquery"); var math = require("math-library"); function Foo() {} // ... module.exports = Foo;
module.exports = { entry: './src/js/main.js', output: { path: __dirname, filename: './build/app.bundle.js', // export itself to a global var libraryTarget: "var", // name of the global var: "Foo" library: "Foo" }, externals: { // require("jquery") is external and available // on the global var jQuery "jquery": "jQuery" },
Bower 사용
ref. 4webpack 설정에 2가지를 추가해 줘야 한다. 그런데 대체로 npm module 를 사용하는 것이 좋다고 webpack 에서 권고한다.
webpack 이 다루기 쉽고, optimize 하기에 npm module 이 좋다고 한다. ref. 4 에 보면 npm 을 이용한것과 bower 를 이용한 것에 대한 compile time 과 bundle size 를 비교해 놨다. 참고하자.
var path = require('path'); var webpack = require('webpack'); module.exports = { entry: './src/main.js', resolve: { modulesDirectories : ["web_modules", "node_modules", "bower_components"] }, plugins: [ new webpack.ResolverPlugin( new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin(".bower.json", ["main"]) ) ], output: { path: __dirname, filename: './build/bundle.js' }, module: { loaders: [ { //tell webpack to use babel-loader for all *.jsx files test: /.jsx?$/, loader: 'babel-loader', exclude: /node_modules/, query: { presets: ['es2015', 'react'] } } ] }, };
댓글 없음:
댓글 쓰기