webpack 保持原目录结构输出_webpack 保持原目录输出 - CSDN
精华内容
参与话题
  • 在学习webpack时,由于资料太少了,网上查询到的资料都是只能将文件...例如我的当前目录结构为, button下的index.jsx编译为当前目录下的index.js grid下的index.jsx编译为当前目录下的index.js 正常情况下配置文件为

    在学习webpack时,由于资料太少了,网上查询到的资料都是只能将文件打包到一个指定目录下。

    查了一下,webpack貌似没有针对每个文件配置编译后的路径。

    没办法,只好自己想办法。

    例如我的当前目录结构为,

    button下的index.jsx编译为当前目录下的index.js
    grid下的index.jsx编译为当前目录下的index.js

    正常情况下配置文件为

    entry : {  

    "button": "demo/button/index.jsx"),  

    "grid": "demo/grid/index.jsx")},

    output: {  

    path: 'dist/js',  

    filename: "[name].js",  

    chunkFilename: "[name].js"

    }

    其实只要小小的变通下,就可以为每个文件配置输出目录,

    entry : {  

    "demo/button": "demo/button/index.jsx"),  

    "demo/grid": "demo/grid/index.jsx")},

    output: {  

    path: 'dist/js',  

    filename: "[name].js",  

    chunkFilename: "[name].js"

    }


    可以看到只是给entry的key加上目录名称就好了。


    展开全文
  • 轻松入门React和Webpack (React 热插拔)

    千次阅读 2016-06-15 10:04:36
    最近在学习React.js,之前都是直接用最原生的方式去写React代码,发现组织起来特别麻烦,之前听人说用Webpack组织React组件得心应手,就花了点时间学习了一下,收获颇丰 说说React 一个组件,有自己的结构,有...

    原文地址:http://www.tuicool.com/articles/BrAVv2y

    最近在学习React.js,之前都是直接用最原生的方式去写React代码,发现组织起来特别麻烦,之前听人说用Webpack组织React组件得心应手,就花了点时间学习了一下,收获颇丰

    说说React

    一个组件,有自己的结构,有自己的逻辑,有自己的样式,会依赖一些资源,会依赖某些其他组件。比如日常写一个组件,比较常规的方式:

    - 通过前端模板引擎定义结构

    - JS文件中写自己的逻辑

    - CSS中写组件的样式

    - 通过RequireJS、SeaJS这样的库来解决模块之间的相互依赖,

    那么在React中是什么样子呢?

    结构和逻辑

    在React的世界里,结构和逻辑交由JSX文件组织,React将模板内嵌到逻辑内部,实现了一个JS代码和HTML混合的JSX。

    结构

    在JSX文件中,可以直接通过 React.createClass 来定义组件:

    var CustomComponent = React.creatClass({
        render: function(){
            return (<div className="custom-component"></div>);
        }
    });

    通过这种方式可以很方便的定义一个组件,组件的结构定义在render函数中,但这并不是简单的模板引擎,我们可以通过js方便、直观的操控组件结构,比如我想给组件增加几个节点:

    var CustomComponent = React.creatClass({
        render: function(){
            var $nodes = ['h','e','l','l','o'].map(function(str){
                return (<span>{str}</span>);
            });
            return (<div className="custom-component">{$nodes}</div>);
        }
    });

    通过这种方式,React使得组件拥有灵活的结构。那么React又是如何处理逻辑的呢?

    逻辑

    写过前端组件的人都知道,组件通常首先需要相应自身DOM事件,做一些处理。必要时候还需要暴露一些外部接口,那么React组件要怎么做到这两点呢?

    事件响应

    比如我有个按钮组件,点击之后需要做一些处理逻辑,那么React组件大致上长这样:

    var ButtonComponent = React.createClass({
        render: function(){
            return (<button>屠龙宝刀,点击就送</button>);
        }
    });

    点击按钮应当触发相应地逻辑,一种比较直观的方式就是给button绑定一个onclick 事件,里面就是需要执行的逻辑了:

    function getDragonKillingSword() {
        //送宝刀
    }
    var ButtonComponent = React.createClass({
        render: function(){
            return (<button onclick="getDragonKillingSword()">屠龙宝刀,点击就送</button>);
        }
    });

    但事实上 getDragonKillingSword() 的逻辑属于组件内部行为,显然应当包装在组件内部,于是在React中就可以这么写:

    var ButtonComponent = React.createClass({
        getDragonKillingSword: function(){
            //送宝刀
        },
        render: function(){
            return (<button onClick={this.getDragonKillingSword}>屠龙宝刀,点击就送</button>);
        }
    });

    这样就实现内部事件的响应了,那如果需要暴露接口怎么办呢?

    暴露接口

    事实上现在 getDragonKillingSword 已经是一个接口了,如果有一个父组件,想要调用这个接口怎么办呢?

    父组件大概长这样:

    var ImDaddyComponent = React.createClass({
      render: function(){
        return (
          <div>
            //其他组件
            <ButtonComponent />
            //其他组件
          </div>
        );
      }
    });
    

    那么如果想手动调用组件的方法,首先在ButtonComponent上设置一个 ref="" 属性来标记一下,比如这里把子组件设置成 <ButtonComponent ref="getSwordButton"/> ,那么在父组件的逻辑里,就可以在父组件自己的方法中通过这种方式来调用接口方法:

    this.refs.getSwordButton.getDragonKillingSword();

    看起来屌屌哒~那么问题又来了,父组件希望自己能够按钮点击时调用的方法,那该怎么办呢?

    配置参数

    父组件可以直接将需要执行的函数传递给子组件:

    <ButtonComponent clickCallback={this.getSwordButtonClickCallback}/>

    然后在子组件中调用父组件方法:

    var ButtonComponent = React.createClass({
        render: function(){
            return (<button onClick={this.props.clickCallback}>屠龙宝刀,点击就送</button>);
        }
    });

    子组件通过 this.props 能够获取在父组件创建子组件时传入的任何参数,因此this.props 也常被当做配置参数来使用

    屠龙宝刀每个人只能领取一把,按钮点击一下就应该灰掉,应当在子组件中增加一个是否点击过的状态,这又应当处理呢?

    组件状态

    在React中,每个组件都有自己的状态,可以在自身的方法中通过 this.state 取到,而初始状态则通过 getInitialState() 方法来定义,比如这个屠龙宝刀按钮组件,它的初始状态应该是没有点击过,所以 getInitialState 方法里面应当定义初始状态 clicked: false 。而在点击执行的方法中,应当修改这个状态值为 click: true :

    var ButtonComponent = React.createClass({
      getInitialState: function(){
        //确定初始状态
        return {
          clicked: false
        };
      },
      getDragonKillingSword: function(){
        //送宝刀
        //修改点击状态
        this.setState({
          clicked: true
        });
      },
      render: function(){
        return (<button onClick={this.getDragonKillingSword}>屠龙宝刀,点击就送</button>);
      }
    });
    

    这样点击状态的维护就完成了,那么render函数中也应当根据状态来维护节点的样式,比如这里将按钮设置为 disabled ,那么render函数就要添加相应的判断逻辑:

    render: function(){
        var clicked = this.state.clicked;
        if(clicked)
            return (<button disabled="disabled" onClick={this.getDragonKillingSword}>屠龙宝刀,点击就送</button>);
        else 
            return (<button onClick={this.getDragonKillingSword}>屠龙宝刀,点击就送</button>);
    }

    小节

    这里简单介绍了通过JSX来管理组件的结构和逻辑,事实上React给组件还定义了很多方法,以及组件自身的生命周期,这些都使得组件的逻辑处理更加强大

    资源加载

    CSS文件定义了组件的样式,现在的模块加载器通常都能够加载CSS文件,如果不能一般也提供了相应的插件。事实上CSS、图片可以看做是一种资源,因为加载过来后一般不需要做什么处理。

    React对这一方面并没有做特别的处理,虽然它提供了Inline Style的方式把CSS写在JSX里面,但估计没有多少人会去尝试,毕竟现在CSS样式已经不再只是简单的CSS文件了,通常都会去用Less、Sass等预处理,然后再用像postcss、myth、autoprefixer、cssmin等等后处理。资源加载一般也就简单粗暴地使用模块加载器完成了

    组件依赖

    组件依赖的处理一般分为两个部分:组件加载和组件使用

    组件加载

    React没有提供相关的组件加载方法,依旧需要通过 <script> 标签引入,或者使用模块加载器加载组件的JSX和资源文件。

    组件使用

    如果细心,就会发现其实之前已经有使用的例子了,要想在一个组件中使用另外一个组件,比如在 ParentComponent 中使用 ChildComponent ,就只需要在ParentComponent 的 render() 方法中写上 <ChildComponent /> 就行了,必要的时候还可以传些参数。

    疑问

    到这里就会发现一个问题,React除了只处理了结构和逻辑,资源也不管,依赖也不管。是的,React将近两万行代码,连个模块加载器都没有提供,更与Angularjs,jQuery等不同的是,他还不带啥脚手架…没有Ajax库,没有Promise库,要啥啥没有…

    虚拟DOM

    那它为啥这么大?因为它实现了一个虚拟DOM(Virtual DOM)。虚拟DOM是干什么的?这就要从浏览器本身讲起

    如我们所知,在浏览器渲染网页的过程中,加载到HTML文档后,会将文档解析并构建DOM树,然后将其与解析CSS生成的CSSOM树一起结合产生爱的结晶——RenderObject树,然后将RenderObject树渲染成页面(当然中间可能会有一些优化,比如RenderLayer树)。这些过程都存在与渲染引擎之中,渲染引擎在浏览器中是于JavaScript引擎(JavaScriptCore也好V8也好)分离开的,但为了方便JS操作DOM结构,渲染引擎会暴露一些接口供JavaScript调用。由于这两块相互分离,通信是需要付出代价的,因此JavaScript调用DOM提供的接口性能不咋地。各种性能优化的最佳实践也都在尽可能的减少DOM操作次数。

    而虚拟DOM干了什么?它直接用JavaScript实现了DOM树(大致上)。组件的HTML结构并不会直接生成DOM,而是映射生成虚拟的JavaScript DOM结构,React又通过在这个虚拟DOM上实现了一个 diff 算法找出最小变更,再把这些变更写入实际的DOM中。这个虚拟DOM以JS结构的形式存在,计算性能会比较好,而且由于减少了实际DOM操作次数,性能会有较大提升

    道理我都懂,可是为什么我们没有模块加载器?

    所以就需要Webpack了

    说说Webpack

    什么是Webpack?

    事实上它是一个打包工具,而不是像RequireJS或SeaJS这样的模块加载器,通过使用Webpack,能够像Node.js一样处理依赖关系,然后解析出模块之间的依赖,将代码打包

    安装Webpack

    首先得有Node.js

    然后通过 npm install -g webpack 安装webpack,当然也可以通过gulp来处理webpack任务,如果使用gulp的话就 npm install --save-dev gulp-webpack

    配置Webpack

    Webpack的构建过程需要一个配置文件,一个典型的配置文件大概就是这样

    var webpack = require('webpack');
    var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js');
    module.exports = {
      entry: {
        entry1: './entry/entry1.js',
        entry2: './entry/entry2.js'
      },
      output: {
        path: __dirname,
        filename: '[name].entry.js'
      },
      resolve: {
        extensions: ['', '.js', '.jsx']
      },
      module: {
        loaders: [{
          test: /\.js$/,
          loader: 'babel-loader'
        }, {
          test: /\.jsx$/,
          loader: 'babel-loader!jsx-loader?harmony'
        }]
      },
      plugins: [commonsPlugin]
    };
    

    这里对Webpack的打包行为做了配置,主要分为几个部分:

    • entry:指定打包的入口文件,每有一个键值对,就是一个入口文件
    • output:配置打包结果,path定义了输出的文件夹,filename则定义了打包结果文件的名称,filename里面的 [name] 会由entry中的键(这里是entry1和entry2)替换
    • resolve:定义了解析模块路径时的配置,常用的就是extensions,可以用来指定模块的后缀,这样在引入模块时就不需要写后缀了,会自动补全
    • module:定义了对模块的处理逻辑,这里可以用loaders定义了一系列的加载器,以及一些正则。当需要加载的文件匹配test的正则时,就会调用后面的loader对文件进行处理,这正是webpack强大的原因。比如这里定义了凡是 .js 结尾的文件都是用 babel-loader 做处理,而 .jsx 结尾的文件会先经过 jsx-loader 处理,然后经过 babel-loader 处理。当然这些loader也需要通过 npm install安装
    • plugins: 这里定义了需要使用的插件,比如commonsPlugin在打包多个入口文件时会提取出公用的部分,生成common.js

    当然Webpack还有很多其他的配置,具体可以参照它的 配置文档

    执行打包

    如果通过 npm install -g webpack 方式安装webpack的话,可以通过命令行直接执行打包命令,比如这样:

    $webpack --config webpack.config.js

    这样就会读取当前目录下的webpack.config.js作为配置文件执行打包操作

    如果是通过gulp插件gulp-webpack,则可以在gulpfile中写上gulp任务:

    var gulp = require('gulp');
    var webpack = require('gulp-webpack');
    var webpackConfig = require('./webpack.config');
    gulp.task("webpack", function() {
        return gulp
            .src('./')
            .pipe(webpack(webpackConfig))
            .pipe(gulp.dest('./build'));
    });

    组件编写

    使用Babel提升逼格

    Webpack使得我们可以使用Node.js的CommonJS规范来编写模块,比如一个简单的Hello world模块,就可以这么处理:

    var React = require('react');
    var HelloWorldComponent = React.createClass({
      displayName: 'HelloWorldComponent',
      render: function() {
        return (<div>Hello world</div>);
      }
    });
    module.exports = HelloWorldComponent;
    

    等等,这和之前的写法没啥差别啊,依旧没有逼格…程序员敲码要有geek范,要逼格than逼格,这太low了。现在都ES6了,React的代码也要写ES6, babel-loader就是干这个的。 Babel 能够将ES6代码转换成ES5。首先需要通过命令 npm install --save-dev babel-loader 来进行安装,安装完成后就可以使用了,一种使用方式是之前介绍的在 webpack.config.js 的loaders中配置,另一种是直接在代码中使用,比如:

    var HelloWorldComponent = require('!babel!jsx!./HelloWorldComponent');

    那我们应当如何使用Babel提升代码的逼格呢?改造一下之前的HelloWorld代码吧:

    import React from 'react';
    export default class HelloWorldComponent extends React.Component {
      constructor() {
        super();
        this.state = {};
      }
      render() {
        return (<div>Hello World</div>);
      }
    }
    

    这样在其他组件中需要引入HelloWorldComponent组件,就只要就可以了:

    import HelloWorldComponent from './HelloWorldComponent'

    怎么样是不是更有逼格了?通过import引入模块,还可以直接定义类和类的继承关系,这里也不再需要 getInitialState 了,直接在构造函数 constructor 中用this.state = xxx 就好了

    Babel带来的当然还不止这些,在其帮助下还能尝试很多优秀的ES6特性,比如箭头函数,箭头函数的特点就是内部的this和外部保持一致,从此可以和 that 、 _this说再见了

    ['H', 'e', 'l', 'l', 'o'].map((c) => {
        return (<span>{c}</span>);
    });

    其他还有很多,具体可以参照 Babel的学习文档

    样式编写

    我是一个强烈地Less依赖患者,脱离了Less直接写CSS就会出现四肢乏力、不想干活、心情烦躁等现象,而且还不喜欢在写Less时候加前缀,平常都是gulp+less+autoprefixer直接处理的,那么在Webpack组织的React组件中要怎么写呢?

    没错,依旧是使用loader

    可以在 webpack.config.js 的loaders中增加Less的配置:

    {
      test: /\.less$/,
      loader: 'style-loader!css-loader!autoprefixer-loader!less-loader'
    }

    通过这样的配置,就可以直接在模块代码中引入Less样式了:

    import React from 'react';
    require('./HelloWorldComponent.less');
    export default class HelloWorldComponent extends React.Component {
      constructor() {
        super();
        this.state = {};
      }
      render() {
        return (<div>Hello World</div>);
      }
    }
    

    其他

    Webpack的loader为React组件化提供了很多帮助,像图片也提供了相关的loader:

    { test: /\.png$/, loader: "url-loader?mimetype=image/png" }

    更多地loader可以移步 webpack的wiki

    在Webpack下实时调试React组件

    Webpack和React结合的另一个强大的地方就是,在修改了组件源码之后,不刷新页面就能把修改同步到页面上。这里需要用到两个库 webpack-dev-server 和 react-hot-loader 。

    首先需要安装这两个库, npm install --save-dev webpack-dev-server react-hot-loader

    安装完成后,就要开始配置了,首先需要修改entry配置:

    entry: {
      helloworld: [
        'webpack-dev-server/client?http://localhost:3000',
        'webpack/hot/only-dev-server',
        './helloworld'
      ]
    },

    通过这种方式指定资源热启动对应的服务器,然后需要配置 react-hot-loader到loaders的配置当中,比如我的所有组件代码全部放在scripts文件夹下:

    {
      test: /\.js?$/,
      loaders: ['react-hot', 'babel'],
      include: [path.join(__dirname, 'scripts')]
    }

    最后配置一下plugins,加上热替换的插件和防止报错的插件:

    plugins: [
      new webpack.HotModuleReplacementPlugin(),
      new webpack.NoErrorsPlugin()
    ]

    这样配置就完成了,但是现在要调试需要启动一个服务器,而且之前配置里映射到http://localhost:3000 ,所以就在本地3000端口起个服务器吧,在项目根目录下面建个server.js:

    var webpack = require('webpack');
    var WebpackDevServer = require('webpack-dev-server');
    var config = require('./webpack.config');
    
    new WebpackDevServer(webpack(config), {
      publicPath: config.output.publicPath,
      hot: true,
      historyApiFallback: true
    }).listen(3000, 'localhost', function (err, result) {
      if (err) console.log(err);
      console.log('Listening at localhost:3000');
    });

    这样就可以在本地3000端口开启调试服务器了,比如我的页面是根目录下地index.html ,就可以直接通过 http://localhost:3000/index.html 访问页面,修改React组件后页面也会被同步修改,这里貌似使用了websocket来同步数据。图是一个简单的效果:

    结束

    React的组件化开发很有想法,而Webpack使得React组件编写和管理更加方便,这里只涉及到了React和Webpack得很小一部分,还有更多的最佳实践有待在学习的路上不断发掘


    展开全文
  • webpack系统配置

    千次阅读 2019-01-20 23:15:54
    简言之,webpack 是一个模块打包器 (module bundler),能够将任何资源如 JavaScript 文件、CSS 文件、图片等打包成一个或少数文件。 为什么要用Webpack? 首先,定义已经说明了 webpack 能将多个资源模块打包成一个...

    简言之,webpack 是一个模块打包器 (module bundler),能够将任何资源如 JavaScript 文件、CSS 文件、图片等打包成一个或少数文件。

    为什么要用Webpack?

    首先,定义已经说明了 webpack 能将多个资源模块打包成一个或少数文件,这意味着与以往的发起多个 HTTP 请求来获得资源相比,现在只需要发起少量的 HTTP 请求。

    其次,webpack 能将你的资源转换为最适合浏览器的“格式”,提升应用性能。比如只引用被应用使用的资源 (剔除未被使用的代码),懒加载资源 (只在需要的时候才加载相应的资源)。再次,对于开发阶段,webpack 也提供了实时加载和热加载的功能,大大地节省了开发时间。除此之外,还有许多优秀之处之处值得去挖掘。不过,webpack 最核心的还是打包的功能。

    webpack,gulp/grunt,npm,它们有什么区别?

    webpack 是模块打包器(module bundler),把所有的模块打包成一个或少量文件,使你只需加载少量文件即可运行整个应用,而无需像之前那样加载大量的图片,css文件,js文件,字体文件等等。而gulp/grunt 是自动化构建工具,或者叫任务运行器(task runner),是把你所有重复的手动操作让代码来做,例如压缩JS代码、CSS代码,代码检查、代码编译等等,自动化构建工具并不能把所有模块打包到一起,也不能构建不同模块之间的依赖图。两者来比较的话,gulp/grunt 无法做模块打包的事,webpack 虽然有 loader 和 plugin可以做一部分 gulp/grunt 能做的事,但是终究 webpack 的插件还是不如 gulp/grunt 的插件丰富,能做的事比较有限。于是有人两者结合着用,将 webpack 放到 gulp/grunt 中用。然而,更好的方法是用 npm scripts 取代 gulp/grunt,npm 是 node 的包管理器 (node package manager),用于管理 node 的第三方软件包,npm 对于任务命令的良好支持让你最终省却了编写任务代码的必要,取而代之的,是老祖宗的几个命令行,仅靠几句命令行就足以完成你的模块打包和自动化构建的所有需求。

    首先看下webpack最基本的应用:

    全局使用

    1、全局安装webpack:

    cnpm install webpack -g

    2、创建项目:

    mkdir app

    在 app 目录下添加 runoob1.js 文件,代码如下:

    document.write("It works.");

    在 app 目录下添加 index.html 文件,代码如下:

    <html>
        <head>
            <meta charset="utf-8">
        </head>
        <body>
            <script type="text/javascript" src="bundle.js" charset="utf-8"></script>
        </body>
    </html>

    接下来我们使用 webpack 命令来打包:

    webpack runoob1.js bundle.js

    执行以上命令会编译 runoob1.js 文件并生成bundle.js 文件,成功后输出信息如下所示:

    Hash: a41c6217554e666594cb
    Version: webpack 1.12.13
    Time: 50ms
        Asset     Size  Chunks             Chunk Names
    bundle.js  1.42 kB       0  [emitted]  main
       [0] ./runoob1.js 29 bytes {0} [built]

    在浏览器中打开 index.html,输出结果如下:

    webpack的使用,最复杂的一部分是莫过于它的配置项。webpack通过你的配置项,放置所有与打包相关的信息。一个基本的配置包括:

    module.exports = {
            entry: '',
            output: {},
            module: {
                rules: []
            },
            plugins: [],
    };

    你如果要打包一个文件,那首先要指定文件的地址,也就是entry;打包之后放在那里呢,也就是output;打包过程中文件要经过怎么样的处理,也就是rules中的loader;如何能够使webpack打包更快,体积更小呢,也就是plugins。这些配置相辅相成,紧密结合。

    安装到本项目:

    npm i -D // npm install --save-dev的简写,是指安装模块并保存到package.json的devDependencies
    npm i webpack -D //安装最新的稳定版本
    npm i webpack@<version> -D // 安装指定版本
    npm i webpack@beta -D // 安装最新的体验版本
    

    安装完成后可以通过以下途径运行安装到本项目的webpack:

    1、根项目目录下对应的命令行里通过".\node_modules\.bin\webpack"(注意:不是"./node_modules/.bin/webpack")运行webpack的可执行文件。 

    2、在npm script里定义的任务会优先使用本项目下的webpack,代码如下:

      "scripts": {
        "start": "webpack --config webpack.config.js"
      }

    虽然介绍了以上两种安装方式, 但是我们推荐安装到本项目,原因是可防止不同的项目因依赖不同版本的webpack而导致冲突。

    模块化:

    1、Common.js:

    // 导出
    
    function moduleA(){
    ...
    }
    
    module.exports = moduleA; 
    
    // 导入
    const moduleA = require("./moduleA ");
    
    

    CommonJS的优点在于:

    a、代码可复用于Node.js环境下并运行,例如做同构应用;

    b、通过NPM发布的很多第三方模块都采用了CommonJS规范。

    CommonJS的缺点:

    这样的代码无法直接运行在浏览器环境下,必须通过工作转换成标准的ES5。

    2、AMD:

    与CommonJS最大的不同是采用了异步的方式去加载依赖的模块。

    采用AMD导入导出的代码如下:

    // 定义一个模块:
    
    define("module", ["dep"], function(dep){
        return exports;
    });
    
    // 导入
    
    require("module", function(module){
    
    })

    AMD的优点:

    a、可在不转换代码的情况下直接在浏览器中运行;

    b、可异步加载依赖;

    c、可并行加载多个依赖;

    d、代码可运行在浏览器环境和Node.js环境下。

    缺点:

    JavaScript运行环境没有原生支持AMD,需要先导入实现了AMD的库后才能正常使用。

    3、es6模块化:

    ES6 模块化是国际标准化组织 ECMA 提出的 JavaScript 模块化规范,它在语言层面上实现了模块化。浏览器厂商和 Node.js都宣布要原生支持该规范 。 它将逐渐取代 CommonJS 和AMD 规范,成为浏览器和服务器通用的模块解决方案 。

    采用 ES6 模块化导入及导出的代码如下:

    //导入
    import { readFile } from ’ fs ’;
    import React from ’ react ’ ;
    
    //导出
    export function hello {) { };
    export default {
        // ...
    };
    

    ES6 模块虽然是终极模块化方案,但它的缺点在于目前无法直接运行在大部分 JavaScript运行环境下,必须通过工具转换成标准的 ES5 后才能正常运行。 

    4. 样式文件申的模块化:

    除了 JavaScript 开始进行模块化改造,前端开发里的样式文件也支持模块化。以 scss 为例,将一些常用的样式片段放进一个通用的文件里,再在另 一个文件里通过@ import 语句导入和使用这些样式片段 :
    // util.scss 文件

    // util.scss 文件
    
    // 定义样式片段
    @mixin center {
        //水平坚直居中
        position: absolute;
        left: 50%;
        top : 50%;
        transform : translate(-50%,-50%);
    }
    
    // main.scss 文件
    
    // 导入和使用 util.scss 中定义的样式片段
    @import "util";
    #box{
        @include center ;
    }

    webpack版本不同,使用方式可能也会不同,下面来看下webpack3的使用:

    一、webpack3的使用:

    普通打包:

    1、首先,创建一个目录,比如test11,使用npm初始化目录:

    npm init

    执行后会有一系列选项,可以按回车键快速确认,完成后会在test11目录生成一个package.json的文件。

    2、之后在本机局部安装webpack:

    npm install webpack@3.10.0 --save-dev

    --save-dev会做为开发依赖来安装webpack。安装成功后,在package.json中会多了一项配置:

    "devDependencies" : {
    
    "webpack": "^3.10.0",
    
    }

    此时的package.json文件为:

    {
      "name": "test11",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "webpack": "^3.10.0"
      }
    }
    

    看到"devDependencies"中含有webpack,就说明已经安装成功了,很快就可以启动一个webpack工程。

    接下来新建一个src目录并且在src目录下新建main.js文件:

    console.log("hello webpack");

    新建一个index.html文件:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="./dist/bundle.js"></script>
    </head>
    <body>
    
    </body>
    </html>

    现在的文件夹目录结构如下:

    执行以下命令打包main.js:

    webpack src/main.js dist/bundle.js

    如果有webpack.config.js文件,则直接输入“webpack”命令即可打包:

    webpack

    其中webpack.config.js配置如下:

    var webpack = require('webpack');
    var path = require('path');                   // 引入node的path模块
    
    module.exports={
        entry: {
            entry: './src/main.js'                 // 入口文件
        },
        output: {
            path: path.resolve(__dirname, 'dist'),  // 打包路劲(获取绝对路径)
            filename: 'bundle.js'                   // 打包文件
        },
        module: {},
        plugins: [],
        devServer: {},
    }

    出现如下信息说明打包成功:

    通过npm run build命令打包:

    修改package.json中的script命令:

    "build": "webpack src/main.js dist/bundle.js"
    {
      "name": "test11",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "build": "webpack src/main.js dist/bundle.js"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "babel-core": "^6.26.3",
        "babel-loader": "^7.1.4",
        "webpack": "^3.10.0"
      }
    }

     然后命令行输入:

    npm run build

    同样也可以打包。

     实现自动监听:

    修改package.json文件中的script命令:

    "watch": "npm run build -- --watch"
    {
      "name": "test11",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "build": "webpack src/main.js dist/bundle.js",
        "watch": "npm run build -- --watch"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "babel-core": "^6.26.3",
        "babel-loader": "^7.1.4",
        "webpack": "^3.10.0"
      }
    }
    

    命令行输入:npm run watch  打包。

     二、webpack4的使用:

    1、首先,创建一个目录,比如demo,使用npm初始化目录:

    npm init

    执行后会有一系列选项,可以按回车键快速确认,完成后会在demo目录生成一个package.json的文件。

    2、之后在本机局部安装webpack:

    npm install webpack --save-dev

    --save-dev会做为开发依赖来安装webpack。安装成功后,在package.json中会多了一项配置:

    "devDependencies" : {
    
     "webpack": "^4.16.3",
    
    }

    此外还需安装webpack-cli:

    npm install webpack-cli --save-dev

    3、配置webpack:

    创建 webpack.config.js 文件作为webpack的配置文件。

    var path = require('path');
    
    var config = {
        entry: {
            main: './main'//配置的单入口,webpack会从main.js开始工作
        },
        output: {
            path: path.join(__dirname, './dist'),//存放打包后文件的输出目录
            publicPath: '/dist/',//指定资源文件的引用目录,
            filename: 'bundle.js'//指定输出文件的名称,只要在html中引用它即可
    
        }
    };
    
    module.exports = config;

    新建index.html文件:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    </head>
    <body>
    <div id='root'></div>
    <script type="text/javascript" src="./dist/bundle.js" charset="utf-8"></script>
    </body>
    </html>

    新建main.js文件:

    document.write("test webpack4.0");

    现在工程目录结构为:

    4、使用webpack:

    设置开发或者生产模式:

    在 webpack.config.js 里设置

    mode: 'development'

    为了方便使用,我们在package.json里加入webpack打包的命令方便我们使用修改script项 :

    "scripts": {
        "build": "npx webpack --config webpack.config.js"
     },

    这样再次运行我们直接输入:

    npm run build 

    就可以了。

    二、webpack.config.js:

    归根结底,webpack就是一个.js配置文件,我们建立一个webpack.config.js文件,我们可以在其中进行各项配置。

    比如我们想要使用一个热加载网页的框架webpack-dev-server(webpack-dev-server是一个轻量级的服务器,修改文件源码后,自动刷新页面将修改同步到页面上),我们先安装:

    npm install webpack@4.6.0 webpack@2.0.15 webpack-dev-server@3.1.3 --save-dev

    然后在package.json的"scripts"里增加一个快速启动webpack-dev-server服务的脚本:

      "scripts": {
    	"dev": "webpack-dev-server --open --config webpack.config.js"
      },

     package.json的详细配置如下:

    {
      "name": "test13",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "build": "webpack",
        "dev": "webpack-dev-server --open --config webpack.config.js"
      },
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "webpack": "^4.6.0",
        "webpack-cli": "^2.0.15",
        "webpack-dev-server": "^3.1.3"
      }
    }

    在webpack.config.js中对webpack.config.js中添加devServer选项对webpack-dev-server进行详细配置:

        devServer:{
            port: 3000,
            publicPath: "./"
        }

     webpack.config.js的详细配置如下:

    var webpack = require('webpack');
    var path = require('path');                   // 引入node的path模块
    
    module.exports={
        entry: {
            entry: './src/main.js'                 // 入口文件
        },
        output: {
            path: path.resolve(__dirname, 'dist'),  // 打包路劲(获取绝对路径)
            filename: 'bundle.js'                   // 打包文件
        },
        module: {},
        plugins: [],
        devServer: {
            open: true,
            port: 8080
        }
    }

    当运行:

    npm run dev

    命令时,就会执行

    webpack-dev-server --open --config webpack.config.js

    其中--config是指向webpac-dev-server读取的配置文件路径,这里直接读取我们在上一步创建的webpack.config.js文件。

    --open会在执行命令时自动在浏览器中打开页面,默认地址是127.0.0.1:8080,不过ip和端口都是可以配的:

    webpack配置中最重要的也是必选的两项是入口和出口,入口的作用是告诉webpack从哪里开始寻找依赖,并且编译。出口则用来配置编译后的文件存储位置和文件名。

    在终端执行

    npm run dev

    就会自动在浏览器中打开页面了。

    三、完善配置文件:

    在webpack的世界里,每一个文件都是一个模块,比如.css,.js,.html,.jpg,.less等。对于不同的模块,需要用不同的加载器来处理,而加载器就是webpack最重要的功能。通过安装不同的加载器可以对各种不同后缀名的文件进行处理,比如现在要写一个css样式,就要用到css-loader和style-loader。用npm方式安装它:

    npm install css-loader --save-dev
    npm install style-loader --save-dev

    安装完成后,在webpack.config.js文件里配置Loader,增加对.css文件的处理:

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

    webpack看似复杂,但它不过是一个js配置文件,只要搞清楚入口、出口、加载器、插件这四个概念,使用起来就不那么困惑了。

    四、Loaders

    Loaders是webpack提供的最激动人心的功能之一了。通过使用不同的loader,webpack有能力调用外部的脚本或工具,实现对不同格式的文件的处理,比如说分析转换scss为css,或者把下一代的JS文件(ES6,ES7)转换为现代浏览器兼容的JS文件,对React的开发而言,合适的Loaders可以把React的中用到的JSX文件转换为JS文件。

    Loaders需要单独安装并且需要在webpack.config.js中的modules关键字下进行配置,Loaders的配置包括以下几方面:

    test:一个用以匹配loaders所处理文件的拓展名的正则表达式(必须)

    loader:loader的名称(必须)

    include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选);
    query:为loaders提供额外的设置选项(可选)

    下面以css的处理为例来说明:

    1、以es6方式导入文件:

    现在写一些css,就需要用到style-loader和css-loader,现在通过npm来安装:

    npm install css-loader style-loader --save-dev

    安装完成后,建立一个css文件:

    body {
      background: pink;
    }

    在main.js文件中增加如下代码:

    import css from './app.css';
    
    console.log("hello world");

    重新执行: 

    npm run build

    文件夹结构如下:

    其中,app.css:

    body {
        background: pink;
    }

    app.js:

    import css from './app.css';
    
    console.log("hello world");

     index.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="./dist/bundle.js"></script>
    </head>
    <body>
    
    </body>
    </html>

    package.json:

    {
      "name": "test16",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "build": "webpack"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "css-loader": "^2.1.0",
        "style-loader": "^0.23.1",
        "webpack": "^3.6.0"
      }
    }
    

     webpack.config.js:

    var webpack = require('webpack');
    var path = require('path');                   // 引入node的path模块
    
    module.exports={
        entry: {
            entry: './src/app.js'                 // 入口文件
        },
        output: {
            path: path.resolve(__dirname, 'dist'),  // 打包路劲(获取绝对路径)
            filename: 'bundle.js'                   // 打包文件
        },
        module: {
            rules: [
                {
                    test: /\.css$/,
                    use: [ 'style-loader', 'css-loader' ]
                }
            ]
        },
        plugins: [],
        devServer: {},
    }

    2、以commonJS方式导入文件:

    文件目录:

    package.json:

    {
      "name": "test25",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "dev": "webpack --config webpack.config.js"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "css-loader": "^2.1.0",
        "style-loader": "^0.23.1",
        "webpack": "^3.6.0"
      }
    }
    

    index.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <div id="app"></div>
    
    <script src="./dist/bundle.js"></script>
    </body>
    </html>

     show.js:

    function show(content){
        document.getElementById("app").innerText = "hello " + content;
    }
    
    module.exports = show;

    style.css:

    body{
        background: pink;
    }
    #app{
        text-align: center;
    }

     main.js:

    const style = require("./style.css");
    const show = require("./show.js");
    show("webpack");

    webpack.config.js:

    const path = require("path");
    
    module.exports = {
        entry: "./src/main.js",
        output: {
            path: path.resolve(__dirname, "dist"),
            filename: 'bundle.js'
        },
        module: {
            rules: [
                {
                    test: /\.css$/,
                    use: ["style-loader", "css-loader"]
                }
            ]
        },
        plugins: [],
        devServer: {}
    };
    

     五、Babel

    Babel其实是一个编译JavaScript的平台,它可以编译代码帮你达到以下目的:

    让你能使用最新的JavaScript代码(ES6,ES7...),而不用管新标准是否被当前使用的浏览器完全支持;

    让你能使用基于JavaScript进行了拓展的语言,比如React的JSX;

    Babel的安装与配置

    Babel其实是几个模块化的包,其核心功能位于称为babel-core的npm包中,webpack可以把其不同的包整合在一起使用,对于每一个你需要的功能或拓展,你都需要安装单独的包(用得最多的是解析Es6的babel-env-preset包和解析JSX的babel-preset-react包)。

    项目目录结构如下:

    package.json:

    {
      "name": "test26",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
    	"dev": "webpack --config webpack.config.js"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "babel-core": "^6.25.0",
        "babel-loader": "^7.1.1",
        "babel-plugin-transform-runtime": "^6.23.0",
        "babel-preset-env": "^1.5.2",
        "babel-preset-es2015": "^6.24.1",
        "webpack": "^3.0.0"
      }
    }
    

    在webpack中配置Babel的方法如下:

    const path = require("path");
    
    module.exports = {
        entry: "./src/main.js",
        output: {
            path: path.resolve(__dirname, "dist"),
            filename: 'bundle.js'
        },
        module: {
            rules: [
                {
                    test: /\.js$/,
                    loader: "babel-loader"
                }
            ]
        },
        plugins: [
    
        ],
        devServer: {}
    };
    

    现在你的webpack的配置已经允许你使用ES6以及JSX的语法了。

    show.js:

    function show(content){
        document.getElementById("app").innerText = "hello, " + content;
    }
    
    module.exports = show;

    main.js:

    let name = "webpack!"
    const show = require("./show.js");
    show(name);

    index.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link rel="stylesheet" href="./dist/main_2d6cab1e.css"/>
    </head>
    <body>
    <div id="app"></div>
    
    <script src="./dist/bundle.js"></script>
    </body>
    </html>

    其中的style.css文件可有可无。

    Babel其实可以完全在 webpack.config.js 中进行配置,但是考虑到babel具有非常多的配置选项,在单一的webpack.config.js文件中进行配置往往使得这个文件显得太复杂,因此一些开发者支持把babel的配置选项放在一个单独的名为 ".babelrc" 的配置文件中。

    六、手动通过终端方式,将第三方库直接打包:

    以jquery为例:

    在对应的文件夹下安装jquery:

    npm install jquery --save-dev

    在main.js中导入jquery:

    var $ = require('jquery');

    导入后就可以开始使用了:

    // main.js
    var $ = require('jquery');
    document.write('<div>Hello World</div>');
    $("div").html("导入jquery实例!");

    文件夹结构及文件内容如下方式一:

    package.json:

    {
      "name": "test15",
      "version": "1.0.0",
      "description": "",
      "main": "webpack.config.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "build": "webpack src/main.js dist/bundle.js"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "jquery": "^3.3.1",
        "webpack": "^3.6.0"
      }
    }
    

    main.js:

    var $ = require('jquery');
    document.write('<div>Hello World</div>');
    $("div").html("导入jquery实例!");

     index.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="./dist/bundle.js"></script>
    </head>
    <body>
    
    </body>
    </html>

    方式二:

    package.json:

    {
      "name": "test15",
      "version": "1.0.0",
      "description": "",
      "main": "webpack.config.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "build": "webpack"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "jquery": "^3.3.1",
        "webpack": "^3.6.0"
      }
    }
    

    webpack.config.js:

    var webpack = require('webpack');
    var path = require('path');                   // 引入node的path模块
    
    module.exports={
        entry: {
            entry: './src/main.js'                 // 入口文件
        },
        output: {
            path: path.resolve(__dirname, 'dist'),  // 打包路劲(获取绝对路径)
            filename: 'bundle.js'                   // 打包文件
        },
        module: {},
        plugins: [],
        devServer: {},
    }

     main.js:

    var $ = require('jquery');
    document.write('<div>Hello World</div>');
    $("div").html("导入jquery实例!");

    index.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="./dist/bundle.js"></script>
    </head>
    <body>
    
    </body>
    </html>

     七、一切皆模块

    Webpack有一个不可不说的优点,它把所有的文件都都当做模块处理,JavaScript代码,CSS和fonts以及图片等等通过合适的loader都可以被处理。

    CSS

    webpack提供两个工具处理样式表,css-loader 和 style-loader,二者处理的任务不同,css-loader使你能够使用类似@import 和 url(...)的方法实现 require()的功能,style-loader将所有的计算后的样式加入页面中,二者组合在一起使你能够把样式表嵌入webpack打包后的JS文件中。

    //安装
    npm install --save-dev style-loader css-loader

    八、插件(Plugins)

    插件(Plugins)是用来拓展Webpack功能的,它们会在整个构建过程中生效,执行相关的任务。
    Loaders和Plugins常常被弄混,但是他们其实是完全不同的东西,可以这么来说,loaders是在打包构建过程中用来处理源文件的(JSX,Scss,Less..),一次处理一个,插件并不直接操作单个文件,它直接对整个构建过程其作用。

    Webpack有很多内置插件,同时也有很多第三方插件,可以让我们完成更加丰富的功能。

    使用插件的方法

    要使用某个插件,我们需要通过npm安装它,然后要做的就是在webpack配置中的plugins关键字部分添加该插件的一个实例(plugins是一个数组)

    下面来看一个实例:通过Plugin将注入bundle.js文件里的css提取到单独的文件中

    项目目录如下:

    app.css:

    body {
        background: pink;
    }
    #app{
        text-align: center;
    }

    show.js:

    function show(content){
        document.getElementById("app").innerText = "hello " + content;
    }
    
    module.exports = show;

    app.js:

    require("./app.css");
    const show = require("./show.js");
    show("Webpack!");

    index.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <div id="app"></div>
    <script src="./dist/bundle.js"></script>
    </body>
    </html>

    package.json:

    {
      "name": "test22",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "start": "webpack --config webpack.config.js"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "css-loader": "^2.1.0",
        "extract-text-webpack-plugin": "^3.0.2",
        "style-loader": "^0.23.1",
        "webpack": "^3.6.0"
      }
    }
    

    webpack.config.js:

    const path = require('path');                   // 引入node的path模块
    const ExtractTextPlugin = require ('extract-text-webpack-plugin');
    
    module.exports={
        // JavaScript 执行入口文件
        entry: {
            entry: './src/app.js'                 // 入口文件
        },
        output: {
            // 将输出文件都放到 dist 目录下
            path: path.resolve(__dirname, './dist'),
            // 将所有依赖的模块合并输出到一个bundle.js文件中
            filename: 'bundle.js'                   
        },
        module: {
            rules: [
                {
                    // 用正则去匹配要用该 loader 转换的 css 文件
                    test: /\.css$/,
                    loaders: ExtractTextPlugin.extract({
                        // 转换.css文件需要使用的Loader
                        use: ['css-loader'],
                    }),
                }
            ]
        },
        plugins: [
                new ExtractTextPlugin({
                // 从.js文件中提取出来的.css文件的名称
                filename:'[name]_[contenthash:8].css',
                }),
        ],
        devServer: {},
    }

    要让以上代码运行起来,需要先安装新引入的插件:

    npm i -D extract-text-webpack-plugin

    安装成功后重新执行构建, 我们会发现 dist 目录下多出一个entry_23c6115b.css 文件, bundle .j s 文件里也没有 css 代码了,再将该 css 文件引入 index.html 里就完成了。

    从以上代码可以看出, Webpack是通过 plugins属性来配置需要使用的插件列表的。plugins 属性是一个数组,里面的每一项都是插件的一个实例,在实例化一个组件时可以通过构造函数传入这个组件支持的配置属性。

    例如,ExtractTextPlugin插件的作用是提取出JavaScript代码里的css到一个单独的文件中。对此我们可以通过插件的filename属性,告诉插件输出的css文件名称是通过[name][contenthash:8] .css字符串模板生成的,里面的[ name]代表文件的名称,[contenthash:8]代表根据文件内容算出的8位Hash值,还有很多配置选项可以在
    ExtractTextPlugin ( https://github.com/webpack-contrib/extract-text-webpack-plugin )的主页上查到。

    九、使用DevServer:

    实际开发中我们可能会需要:

    • 提供 HTTP 服务而不是使用本地文件预览;
    • 监昕文件的变化并自动刷新网页,做到实时预览:
    • 支持 Source Map ,以方便调试。

    对于这些, Webpack 都为我们考虑好了。 Webpack 原生支持上述第 2 、 3 点内容,再结合官方提供的开发工具 DevServer ( https://webpack乒org/configuration/dev-server/)也可以很方便地做到第 1 点。 DevServer 会启动一个 HTTP 服务器用于服务网页请求,同时会帮助启动Webpack,并接收 Webpack 发出的文件更变信号,通过 WebSocket 协议自动刷新网页做到实时预览。

    下面在一个小项目中集成DevServer。

    项目目录如下:

    app.css:

    body {
        background: pink;
    }
    #app{
        text-align: center;
    }

    app.js:

    require("./app.css");
    const show = require("./show.js");
    show("Webpack!");

     show.js:

    function show(content){
        document.getElementById("app").innerText = "hello " + content;
    }
    
    module.exports = show;

    index.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <div id="app"></div>
    <script src="./dist/bundle.js"></script>
    </body>
    </html>

     package.json:

    {
      "name": "test23",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "start": "webpack --config webpack.config.js",
        "server": "webpack-dev-server --open"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "css-loader": "^2.1.0",
        "extract-text-webpack-plugin": "^3.0.2",
        "style-loader": "^0.23.1",
        "webpack": "^3.6.0",
        "webpack-dev-server": "^2.9.1"
      }
    }
    

    首先需要安装 DevServer:

    cnpm i -D webpack-dev-server

    webpack.config.js:

    const path = require('path');                   // 引入node的path模块
    const ExtractTextPlugin = require ('extract-text-webpack-plugin');
    
    module.exports={
        // JavaScript 执行入口文件
        entry: {
            entry: './src/app.js'                 // 入口文件
        },
        output: {
            // 将输出文件都放到 dist 目录下
            path: path.resolve(__dirname, './dist'),
            // 将所有依赖的模块合并输出到一个bundle.js文件中
            filename: 'bundle.js'
        },
        module: {
            rules: [
                {
                    // 用正则去匹配要用该 loader 转换的 css 文件
                    test: /\.css$/,
                    loaders: ExtractTextPlugin.extract({
                        // 转换.css文件需要使用的Loader
                        use: ['css-loader'],
                    }),
                }
            ]
        },
        plugins: [
            new ExtractTextPlugin({
                // 从.js文件中提取出来的.css文件的名称
                filename:'[name]_[contenthash:8].css',
            }),
        ],
        devServer: {},
    }

     安装成功后执行 "webpack-dev- server"或"npm run server" 命令, DevServer 就启动了,这时我们会看到控制台有一 串日志输出:

    Project is running at http://localhost:8080/
    webpack output is served from /

    这意味着 DevServer 启动的 HTTP 服务器监听在 8080 端口, DevServer 启动后会一直驻留在后台保持运行 ,访问这个网址,就能获取项目根目录下的 index.html 了。用浏览器打开这个地址时我们会发现页面空白,错误的原因是./ dist/bundle.j s 加载 404了 。 同时我们会发现并没有文件输出到 dist 目录,原因是 DevServer 会将 Webpack 构建出的文件保存在 内存中,在要访问输出的文件时,必须通过 HTTP 服务访问。由于 DevServer不会理会 webpack .config.j s 里配置的 output .path 属性,所以要获取 bundle.js 的正确 URL 是 http: //loca lhost:8080/bundle.js,对应的 index.html 应该修改为:

    <html>
    <head>
    <meta charset= ” UTF-8 ” >
    </head>
    <body>
    <div id=” app ”></ div>
    〈 !一导入 DevServer 输出的 JavaScript 文件一〉
    <script src=”bundle.js"></script>
    </body>
    </ html>

    模块热替换:

    除了通过重新刷新整个网页来实现实时预览, DevServer 还有一种被称作模块热替换的刷新技术。模块热替换能做到在不重新加载整个网页的情况下,通过将己更新的模块替换老模块,再重新执行一次来实现实时预览。模块热替换相对于默认的刷新机制能提供更快的响应速度和更好的开发体验。模块热替换默认是关闭的,要开启模块热替换,我们只 需在启动DevServer 时带上一 hot 参数,重启 DevServer 后再去更新文件就能体验到模块热替换的神奇了。 

    支持 Source Map:

    在浏览器中运行的 JavaScript 代码都是编译器输出的代码,这些代码的可读性很差。如果在开发过程中遇到一个不知道原因的 Bug,则我们可能需要通过断点调试去找出问题。在编译器输出的代码上进行断点调试是一件辛苦和不优雅的事情,调试工具可以通过 Source Map( https://www.html5rocks.com/en/tutorials/developertools/sourcemaps/)映射代码,让我们在源代码上断点调试。 Webpack 支持生成 Source Map,只需在启动时带上一 devtool source-map参数。重启 DevServer 后刷新页面,再打开 Chrome 浏览器的开发者工具,就可以在 Sources栏中看到可调试的源代码了,如图1-2所示。

    展开全文
  • 编写一个webpack插件

    千次阅读 2016-12-11 22:55:04
    插件将Webpack引擎的全部潜能暴露给第三方开发人员。使用分段构建回调,开发人员可以将自己的行为引入Webpack构建过程。插件的开发比起加载器(loaders)的开发更近了一步,因为你需要用一些webpack底层的内部构建来...

    插件将Webpack引擎的全部潜能暴露给第三方开发人员。使用分段构建回调,开发人员可以将自己的行为引入Webpack构建过程。插件的开发比起加载器(loaders)的开发更近了一步,因为你需要用一些webpack底层的内部构件来钩住他们。准备去阅读一下webpack的源码吧。

    编译器(compiler)和编译(compilation)

    在开发插件时,两个最重要的对象是:compiler和compilation。了解他们的角色是扩展Webpack引擎的重要的第一步。

    • compiler对象表示完全配置的webpack环境,这个对象在webpack启动时,就会创建,并配置了所有操作设置包括options,loaders,plugins等。当一个插件应用于Webpack环境时,插件将接收对此compiler的一个引用, 使用compiler去访问Webpack主环境。
    • 一个compilation对象表示一个单一构建的版本花资产(asset)。在运行Webpack开发中间件时,每次检测到文件更改时都将创建一个新的编译,从而生成一组新的已编译资产。一个compilation表达有compilation还提供了许多回调点,插件可以选择执行自定义操作。

    这两个组件是任何Webpack插件(特别是一个编译)的一个组成部分,因此开发人员要熟悉这些源文件:

    基本结构

    插件是在它们的原型上具有apply方法的可实例化对象。这个apply方法在安装插件时由Webpack编译器调用一次。 apply方法被引用到底层的Webpack compiler,它允许访问compiler回调。 一个简单的插件的结构如下:

    function HelloWorldPlugin(options) {
      // Setup the plugin instance with options...
    }
    
    HelloWorldPlugin.prototype.apply = function(compiler) {
      compiler.plugin('done', function() {
        console.log('Hello World!'); 
      });
    };
    
    module.exports = HelloWorldPlugin;

    然后安装插件,只需在你的Webpack config plugins数组中包含一个实例:

    var HelloWorldPlugin = require('hello-world');
    
    var webpackConfig = {
      // ... config settings here ...
      plugins: [
        new HelloWorldPlugin({options: true})
      ]
    };

    访问编译对象

    使用编译器对象,您可以绑定回调,为每个新编译提供引用。 这些编译提供了用于在构建过程中挂钩到许多步骤的回调。

    function HelloCompilationPlugin(options) {}
    
    HelloCompilationPlugin.prototype.apply = function(compiler) {
    
      // Setup callback for accessing a compilation:
      compiler.plugin("compilation", function(compilation) {
    
        // Now setup callbacks for accessing compilation steps:
        compilation.plugin("optimize", function() {
          console.log("Assets are being optimized.");
        });
      });
    };
    
    module.exports = HelloCompilationPlugin;

    有关编译器,编译和其他重要对象可用的回调的更多信息,请参阅插件API文档。

    异步编译插件

    一些编译插件步骤是异步的,并且传递一个回调函数,当插件完成运行时必须调用它。

    function HelloAsyncPlugin(options) {}
    
    HelloAsyncPlugin.prototype.apply = function(compiler) {
      compiler.plugin("emit", function(compilation, callback) {
    
        // Do something async...
        setTimeout(function() {
          console.log("Done with async work...");
          callback();
        }, 1000);
    
      });
    };
    
    module.exports = HelloAsyncPlugin;

    一个简单的示例

    一旦我们可以锁定到Webpack编译器和每个单独的编译,,我们可以用webpack引擎做我们想做的事情。我们可以重新格式化现有文件,创建衍生文件或制作全新的资产。
    让我们编写一个简单的示例插件,生成一个名为filelist.md的新构建文件; 其内容将列出我们构建中的所有资产文件。 这个插件可能看起来像这样:

    function FileListPlugin(options) {}
    
    FileListPlugin.prototype.apply = function(compiler) {
      compiler.plugin('emit', function(compilation, callback) {
        // Create a header string for the generated file:
        var filelist = 'In this build:\n\n';
    
        // Loop through all compiled assets,
        // adding a new line item for each filename.
        for (var filename in compilation.assets) {
          filelist += ('- '+ filename +'\n');
        }
    
        // Insert this list into the Webpack build as a new file asset:
        compilation.assets['filelist.md'] = {
          source: function() {
            return filelist;
          },
          size: function() {
            return filelist.length;
          }
        };
    
        callback();
      });
    };
    
    module.exports = FileListPlugin;

    有用的插件模式

    插件给予了无限的机会在Webpack构建系统中执行自定义。这允许您创建自定义资产类型,执行唯一的构建修改,甚至在使用中间件时增强Webpack运行时。以下是在编写插件时非常有用的Webpack的一些功能。

    浏览资产,块,模块和依赖关系

    在编译被密封之后,编译中的所有结构可以被遍历。

    function MyPlugin() {}
    
    MyPlugin.prototype.apply = function(compiler) {
      compiler.plugin('emit', function(compilation, callback) {
    
        // Explore each chunk (build output):
        compilation.chunks.forEach(function(chunk) {
          // Explore each module within the chunk (built inputs):
          chunk.modules.forEach(function(module) {
            // Explore each source file path that was included into the module:
            module.fileDependencies.forEach(function(filepath) {
              // we've learned a lot about the source structure now...
            });
          });
    
          // Explore each asset filename generated by the chunk:
          chunk.files.forEach(function(filename) {
            // Get the asset source for each file generated by the chunk:
            var source = compilation.assets[filename].source();
          });
        });
    
        callback();
      });
    };
    
    module.exports = MyPlugin;
    • compilation.modules:一个编译中的模块数组(构建输入)。 每个模块管理一个源库中的原文件的构建。
    • module.fileDependencies:包含在模块中的源文件路径数组。这包括源JavaScript文件本身(例如:index.js)和所需的所有依赖关系资产文件(样式表,图像等)。 查看依赖关系对于查看哪些源文件属于模块很有用。
    • compilation.chunks: 编译中的块数组(构建输出)。 每个块管理最终渲染资产的组成。
    • chunk.modules: 包含在块中的模块数组。通过扩展,您可以查看每个模块的依赖关系来查看馈入到一个块中的原始源文件。
    • chunk.files:由块生成的输出文件名的数组。 您可以从compilation.assets表中访问这些资产来源。

    监控文件监视图

    在运行Webpack中间件时,每个编译包括一个fileDependencies数组(什么文件被监视)和一个fileTimestamps哈希,将观察到的文件路径映射到时间戳。这些对于检测编译期间已更改的文件非常有用:

    function MyPlugin() {
      this.startTime = Date.now();
      this.prevTimestamps = {};
    }
    
    MyPlugin.prototype.apply = function(compiler) {
      compiler.plugin('emit', function(compilation, callback) {
    
        var changedFiles = Object.keys(compilation.fileTimestamps).filter(function(watchfile) {
          return (this.prevTimestamps[watchfile] || this.startTime) < (compilation.fileTimestamps[watchfile] || Infinity);
        }.bind(this));
    
        this.prevTimestamps = compilation.fileTimestamps;
        callback();
      }.bind(this));
    };
    
    module.exports = MyPlugin;

    您还可以将新的文件路径馈送到监视图中,以在这些文件更改时接收编译触发器。 只需将有效的文件路径推送到compilation.fileDependencies数组中,即可将它们添加到watch中。 注意:fileDependencies数组在每个编译中重建,因此您的插件必须将自己的监视依赖项推送到每个编译中,以保持它们被观察。

    更改块

    与监视图类似,通过跟踪它们的哈希来监视编译中的改变块(或模块)是相当简单的。

    function MyPlugin() {
      this.chunkVersions = {};
    }
    
    MyPlugin.prototype.apply = function(compiler) {
      compiler.plugin('emit', function(compilation, callback) {
    
        var changedChunks = compilation.chunks.filter(function(chunk) {
          var oldVersion = this.chunkVersions[chunk.name];
          this.chunkVersions[chunk.name] = chunk.hash;
          return chunk.hash !== oldVersion;
        }.bind(this));
    
        callback();
      }.bind(this));
    };
    
    module.exports = MyPlugin;
    展开全文
  • 如何打造一个自定义的bootstrap? 前言 一般我们用bootstrap呐,都是用的从官网或github下载下来build好了的版本,千人一脸呐多没意思。当然,官网也给我们提供了自定义的工具,如下图所示,但每次要改些什么就要...
  • React Native 的出现,让前端工程师拥有了使用 JavaScript 编写原生 APP 的能力。相比之前的 Web app 来说,对于性能和用户体验提升了非常多。 但是 React Native 的代码只兼容两个平台(iOS 和 Android),并没有...
  • webpack4 搭建 Vue 开发环境笔记

    千次阅读 2018-11-14 09:45:17
    一、node 知识 __dirname: 获取当前文件所在路径,等同于 path.dirname(__filename) console.log(__dirname); // Prints: /Users/mjr console.log(path.dirname(__filename)); // Prints: /Users/mjr ...
  • React 全家桶构建后台管理平台

    千次阅读 2017-08-19 23:38:42
    React 全家桶实现后台管理界面 一、实验介绍 1.1 实验内容 React 是一个 Facebook 和 Instagram 用来创建用户界面的 JavaScript 库。创造 React 是为了解决一个问题:构建随着时间数据“不断变化”的“大规模”...
  • vue老项目升级vue-cli3.0问题总结

    千次阅读 2019-07-22 15:46:06
    2、删除新项目中src下的内容,把项目中src目录覆盖到新项目中 3、把router从目录文件夹改为文件,src/router/index.js提高一层变成src/router.js 4、我的项目中src已经分为了views和components所以无需修改,如果...
  • Vue+ElementUI项目使用webpack输出MPA

    千次阅读 2019-08-27 15:44:16
    【摘要】 Vue+ElementUI多页面打包改造 ... 一.... 为另一个项目提供可嵌入的功能单页,大部分页面使用时都是独立功能页,个别页面带有左侧边栏(相当于3-4个页面的整合形态),由于资源定位地址的限定,每个页面打包为...
  • webpack多页应用架构专题系列 4

    千次阅读 2016-10-25 10:18:43
    第四章:webpack的进阶应用 如何打造一个自定义的bootstrap? 前言 一般我们用bootstrap呐,都是用的从官网或github下载下来build好了的版本,千人一脸呐多没意思。当然,官网也给我们提供了自定义的工具,如下图...
  • react开发:从零开始搭建一个react项目

    万次阅读 多人点赞 2017-06-29 17:48:41
    从头开始建立一个React App - 项目基本配置 npm init 生成 package.json 文件.... npm install ... --save react - 安装React.npm install ... --save react-dom 安装React Dom,这个包是用来处理virtual DOM。...
  • Webpack 5 正式发布

    千次阅读 2020-10-13 18:33:30
    Webpack简介 随着前端发展如日冲天,前端项目也越来越复杂,得益于Nodejs的发展,前端模块化、组件化、工程化也大势所趋。伴随着前端的模块化和工程化,Grunt、Gulp到Webpack等项目构建和打包工具也随之出现。 前端...
  • 博客园地址:《大史住在大前端》原创博文目录 一. 需求分析 为另一个项目提供可嵌入的功能单页,大部分页面使用时都是独立功能页,个别页面带有左侧边栏(相当于3-4个页面的整合形态),由于资源定位地址的限定,...
  • gulp + webpack 构建多页面前端项目

    千次阅读 2016-12-02 13:55:55
    修改增加了demo地址 gulp-webpack-demo 之前在使用gulp和webpack对项目进行构建的时候遇到了一些问题,最终算是搭建了一套比较完整的解决方案,接下来这篇文章以一个实际项目为例子,讲解多页面项目中如何利用...
  • 使用webpack和babel搭建react开发环境

    千次阅读 2017-04-21 20:22:42
    写在前面,使用webpack已经蛮长一段时间,但是在新项目开始之际,都是东拼西凑其他项目的配置来使用,如果要自己从零开始写一个完整项目的webpack配置估计得费死劲,所以在webpack发布2.x版本之际,正是时候来认真
  • 基于Vue JS, Webpack 以及Material Design的渐进式web应用 [Part 1]原文:基于Vue JS, Webpack 以及Material Design的渐进式web应用 [Part 1]译者:neal1991welcome to star my articles-translator , providing you...
  • 《深入浅出Webpack

    2018-09-10 19:08:04
    第1章 入门 (1)模块化 1.CommonJS CommonJS是一种被广泛使用的Javascript模块化规范,其核心思想是通过require方法来同步加载依赖的其他模块,通过module.exports导出需要暴露的接口。 2.AMD ...
  • 但是限制于单页应用的一些缺点,比如:seo、首屏时间等因素,很多应用的结构还是保持了多页面结构。此篇讲述的是如何在多页面应用结构的基础上,利用webpack生成带hashcode文件名的方式实现静态资源的增量更新方案。...
  • 关注前端达人,与你共同进步开篇关于 React、TypeScript 的介绍和其在前端的重要地位,想必大家都很清楚吧,在这里笔者就不介绍了。你也许会问为什么要基于 Typ...
1 2 3 4 5 ... 20
收藏数 1,827
精华内容 730
关键字:

webpack 保持原目录结构输出