webpack、sass、react、redux的简易demo入门

最开始只是想综合的初略学习下这几种技术,所以萌生了搭建一个简单的结构的想法;整个结构应该没有什么问题;已经放到了github上,地址:webpack、sass、react、redux的简易demo入门

webpack

本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。

我自己对webpack的使用也不是很熟练,还没有很明白webpack的设计思维,所以在配置的时候也是一脸懵逼,跟着官方文档和一些社区的文章来一步一步配置,主要还是需要知道这几个配置:

  • 入口(entry)
  • 输出(output)
  • loader
  • 插件(plugins)
  • 解析(Resolve)

入口(entry)

起点或是应用程序的起点入口。从这个起点开始,应用程序启动执行。如果传递一个数组,那么数组的每一项都会执行。

entry字段可以是string | [string] | object { <key>: string | [string] }或者是函数【函数返回值是上面的三种类型中的一种】,本次demo的选择是对象来处理,如下:

entry: {
        home: './src/scripts/pages/home/index.js'
        // homecss: './src/styles/pages/home.scss'  // scss入口放到最开始的js中, webpack无法处理非js文件
    },

输出(output)

output 位于对象最顶级键(key),包括了一组选项,指示 webpack 如何去输出、以及在哪里输出你的「bundle、asset 和其他你所打包或使用 webpack 载入的任何内容」。

通常而言,通过 output.filename 和 output.path 属性,来告诉 webpack bundle 的名称,以及我们想要生成(emit)到哪里。demo中的配置如:

output: {
        path: path.join(__dirname + '/src/dist'),
        // publicPath:'/'
        // publicPath: path.join(__dirname + '/src/dist/'), // 加载外部资源,此选项指定在浏览器中所引用的「此输出目录对应的公开 URL」
        filename: 'js/[name].js'
    }

需要注意的是,filename: 'js/[name].js'这么写是因为webpack支持入口文件为多个文件时可以配置不同的输出文件名,参考output-filename

loader

loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。本质上,webpack loader 将所有类型的文件,转换为应用程序的依赖图可以直接引用的模块。

重要的是要记得,在 webpack 配置中定义 loader 时,要定义在 module.rules 中,而不是 rules。

module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /(node_modules|bower_components)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env', '@babel/stage-3', '@babel/react']
                    }
                }
            },
            {
                test: /\.scss$/,
                use: extractSass.extract({
                    use: [{
                        loader: "css-loader"
                    }, {
                        loader: "sass-loader"
                    }],
                    // publicPath: './../', // 加载外部资源,把路径重新定义到dist目录下,而不是css里面
                    // use style-loader in development
                    fallback: "style-loader"
                })
            },
            {
                test: /\.(png|svg|jpg|gif|jpeg)$/,
                use: [
                    {   
                        loader: 'file-loader',
                        options: {
                            name: '[name].[ext]',
                            useRelativePath: true, // 图片相对路径
                            outputPath: 'images/'
                        }
                    }
                ]
            }
            ]
    }

以上是demo的配置,在 webpack 的配置中 loader 有两个目标。

  1. 识别出应该被对应的 loader 进行转换的那些文件。(使用 test 属性)
  2. 转换这些文件,从而使其能够被添加到依赖图中(并且最终添加到 bundle 中)(use 属性)

可以看到,同一个文件可以顺序使用多个loader来加载,每个loader可以传递参数来进行特殊处理。

插件(plugins)

插件是 wepback 的支柱功能。webpack 自身也是构建于,你在 webpack 配置中用到的相同的插件系统之上!插件目的在于解决 loader 无法实现的其他事。

webpack 插件是一个具有 apply 属性的 JavaScript 对象。apply 属性会被 webpack compiler 调用,并且 compiler 对象可在整个编译生命周期访问。

由于插件可以携带参数/选项,你必须在 webpack 配置中,向 plugins 属性传入 new 实例。根据你的 webpack 用法,这里有多种方式使用插件。插件使用插件列表

demo中使用了两个pluginsextract-text-webpack-plugin & DefinePlugin,都是按照官方插件文档配置的。

解析(Resolve)

这些选项能设置模块如何被解析。webpack 提供合理的默认值,但是还是可能会修改一些解析的细节。关于 resolver 具体如何工作的更多解释说明,请查看模块解析方式。

简单的说,就是会帮我们弄好一些默认的选项,比如别名aliasextensions模块默认后缀、mainFiles默认文件入口等;

resolve: {
        mainFiles: ["index"],
        extensions: ['.js', '.jsx'],
        alias: {
            'stylepages': path.join(__dirname + '/src/styles/pages'),
            'scripts': path.join(__dirname + '/src/scripts/pages'),
            'commonjs': path.join(__dirname + '/src/scripts/common')
        }
    }

遇到问题

关于webpack无法把css文件作为入口,需要在js中引入css,然后进行css的分离

关于sass使用@import时来使用alias别名配置的路径时不被正确解析,sass-loader需要在使用别名的前面加上~,stylelib是别名,使用时@import '~stylelib/base.scss';

关于sass和图片等外部资源的打包出现的路径不一致,,有以下几种方法:

  • 第一种方法,可以在配置fileloader的时候使用相对路径:useRelativePath
{
    test: /\.(png|svg|jpg|gif|jpeg)$/,
         use: [
           {   
              loader: 'file-loader',
               options: {
                   name: '[name].[ext]',
                   useRelativePath: true, // 图片相对路径
                   outputPath: 'images/'
                 }
              }
          ]
}
  • 第二种方法,在使用ExtractTextWebpackPlugin的时候,可以根据已有的loader来重新提取loader,在提取新的loader时,可以配置公共路径publicPath
const ExtractTextPlugin = require("extract-text-webpack-plugin");

const extractSass = new ExtractTextPlugin({
    filename: "css/[name].css",
    disable: process.env.NODE_ENV === "development"
});


// loader 里面
{
   test: /\.scss$/,
   use: extractSass.extract({
         use: [{
                loader: "css-loader"
              }, {
                 loader: "sass-loader"
              }],
       publicPath: './../', // 把路径重新定义到dist目录下,而不是css里面
       // use style-loader in development
       fallback: "style-loader"
    })
}
  • 第三种方法,直接在所有的输出目录处使用当前的打包路径,publicPath
output: {
     path: path.join(__dirname + '/src/dist'),
     publicPath:'./'
     // publicPath: path.join(__dirname + '/src/dist/'), // 加载外部资源,此选项指定在浏览器中所引用的「此输出目录对应的公开 URL」
     filename: 'js/[name].js'
    }

sass

Sass 是一款强化 CSS 的辅助工具,它在 CSS 语法的基础上增加了变量 (variables)、嵌套 (nested rules)、混合 (mixins)、导入 (inline imports) 等高级功能,这些拓展令 CSS 更加强大与优雅。使用 Sass 以及 Sass 的样式库(如 Compass)有助于更好地组织管理样式文件,以及更高效地开发项目。

使用sass可以感受到以下几点的优势:

  1. 合理的嵌套规则,不需要重复书写selector。
  2. 变量$、占位符%都可以很好的提供复用性
  3. 函数、运算可以很好的增加灵活性

这里就不多说sass的学习了,可以查看sass中文sass官网

react

React 是一个采用声明式,高效而且灵活的用来构建用户界面的框架。

学习react的重要内容包括:

  1. jsx的书写语法
  2. 组件Components【props、state、Lifecycle…】
  3. 虚拟dom,
  4. 组件渲染和diff,Lists and Keys

更多的学习react学习官网

redux

Redux 是 JavaScript 状态容器,提供可预测化的状态管理。应用中所有的 state 都以一个对象树的形式储存在一个单一的 store 中。 惟一改变 state 的办法是触发 action,一个描述发生什么的对象。 为了描述 action 如何改变 state 树,你需要编写 reducers;

  1. 应用中所有的 state 都以一个对象树的形式储存在一个单一的 store 中,初始时由reducer初识态来创建
  2. 改变state的唯一方法就是触发dispatch一个action,
  3. 一个action是本质上一个对象,用来把最新的数据传到store的有效载荷
  4. Reducer就是把action这个有效载荷解析,并应用到store之中;

具体实践中:

  1. 书写reducer,来确定初始时的state
  2. 根据初始reducer来创建store,并使用store.subscribe来监听store变化时的操作
  3. 书写action,来承载每次修改store的变化
  4. 在交互等情况使用dispatch一个相应的action,
  5. reducer来响应action,并更新store,[store.subscribe监听函数将会被执行来驱动页面变化]
import { createStore } from 'redux';

/**
 * 这是一个 reducer,形式为 (state, action) => state 的纯函数。
 * 描述了 action 如何把 state 转变成下一个 state。
 *
 * state 的形式取决于你,可以是基本类型、数组、对象、
 * 甚至是 Immutable.js 生成的数据结构。惟一的要点是
 * 当 state 变化时需要返回全新的对象,而不是修改传入的参数。
 *
 * 下面例子使用 `switch` 语句和字符串来做判断,但你可以写帮助类(helper)
 * 根据不同的约定(如方法映射)来判断,只要适用你的项目即可。
 */
function counter(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT':
    return state + 1;
  case 'DECREMENT':
    return state - 1;
  default:
    return state;
  }
}

// 创建 Redux store 来存放应用的状态。
// API 是 { subscribe, dispatch, getState }。
let store = createStore(counter);

// 可以手动订阅更新,也可以事件绑定到视图层。
store.subscribe(() =>
  console.log(store.getState())
);

// 改变内部 state 惟一方法是 dispatch 一个 action。
// action 可以被序列化,用日记记录和储存下来,后期还可以以回放的方式执行
store.dispatch({ type: 'INCREMENT' });
// 1
store.dispatch({ type: 'INCREMENT' });
// 2
store.dispatch({ type: 'DECREMENT' });
// 1

异步redux

像 redux-thunk 或 redux-promise 这样支持异步的 middleware 都包装了 store 的 dispatch() 方法,以此来让你 dispatch 一些除了 action 以外的其他内容,例如:函数或者 Promise。

redux-react

Redux 官方提供的 React 绑定库。 具有高效且灵活的特性。

结合react来完善了下redux的使用,不需要我们每次都使用store.subscribe来监听store的变化,提供了新的接口:

<Provider store>
可以在最顶层将store传递至每个需要store的子组件

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
可以将一些state映射到某个组件,来驱动组件渲染

总结

react-redux-demo

需要webpack3以上

运行方式:

`npm install`  安装相关依赖
`webpack` 执行webpack来进行打包  

在浏览器中打开目录下的/html/pages/home.html文件即可访问结果

➜  react-redux-demo git:(master) tree -I node_modules 
.
├── html
│   └── pages
│       └── home.html
├── package-lock.json
├── package.json
├── readMe.md
├── src
│   ├── dist
│   │   ├── css
│   │   │   └── home.css

│   │   ├── images
│   │   │   └── head.jpeg
│   │   └── js
│   │       └── home.js
│   ├── scripts
│   │   ├── common
│   │   └── pages
│   │       └── home
│   │           ├── actions
│   │           │   ├── data.js
│   │           │   └── index.js
│   │           ├── components
│   │           │   ├── app.js
│   │           │   ├── article
│   │           │   │   └── index.js
│   │           │   └── consults
│   │           │       └── index.js
│   │           ├── index.js
│   │           ├── reducers
│   │           │   ├── data.js
│   │           │   └── index.js
│   │           └── store
│   │               └── index.js
│   └── styles
│       ├── images
│       │   └── home
│       │       └── head.jpeg
│       ├── lib
│       │   └── base.scss
│       ├── module
│       └── pages
│           └── home.scss
└── webpack.config.js
    原文作者:caoweiju
    原文地址: https://segmentfault.com/a/1190000012862993
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞