webpack服务器 也可以作为本地服务器吗

在 SegmentFault,解决技术问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。
一线的工程师、著名开源项目的作者们,都在这里:
获取验证码
已有账号?
问题对人有帮助,内容完整,我也想知道答案
问题没有实际价值,缺少关键内容,没有改进余地
现在图片可以加载,但是还是不能显示,我把loadTexture里面的图片路径写成绝对路径也不行 我试着换了其他图片但是还不起作用,不是图片问题,THREE.TextureLoader写法也是对的啊
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
这两天有空我就想这个问题,既然本地服务器能够获取到图片为什么不能显示然后有了收获,map is not defined于是干脆把 map:THREE.ImageUtils.loadTexture(texture)写到里面,奇迹发生了
分享到微博?
关闭理由:
删除理由:
忽略理由:
推广(招聘、广告、SEO 等)方面的内容
与已有问题重复(请编辑该提问指向已有相同问题)
答非所问,不符合答题要求
宜作评论而非答案
带有人身攻击、辱骂、仇恨等违反条款的内容
无法获得确切结果的问题
非开发直接相关的问题
非技术提问的讨论型问题
其他原因(请补充说明)
我要该,理由是:14055人阅读
开发工具(3)
Webpack是目前基于React和Redux开发的应用的主要打包工具。我想使用Angular 2或其他框架开发的应用也有很多在使用Webpack。
当我第一次看到Webpack的配置文件时,它看起来非常的陌生,我非常的疑惑。经过一段时间的尝试之后我认为这是因为Webpack只是使用了比较特别的语法和引入了新的原理,因此会让使用者感到疑惑。这些也是导致Webpack不被人熟悉的原因。
因为刚开始使用Webpack很让人疑惑,我觉得有必要写几篇介绍Webpack的功能和特性的文章以帮助初学者快速理解。此文是最开始的一篇。
Webpack的两个最核心的原理分别是:
1. 一切皆模块
正如js文件可以是一个“模块(module)”一样,其他的(如css、image或html)文件也可视作模 块。因此,你可以require('myJSfile.js')亦可以require('myCSSfile.css')。这意味着我们可以将事物(业务)分割成更小的易于管理的片段,从而达到重复利用等的目的。
2. 按需加载
传统的模块打包工具(module bundlers)最终将所有的模块编译生成一个庞大的bundle.js文件。但是在真实的app里边,“bundle.js”文件可能有10M到15M之大可能会导致应用一直处于加载中状态。因此Webpack使用许多特性来分割代码然后生成多个“bundle”文件,而且异步加载部分代码以实现按需加载。
好了,下面来看看那些令人困惑的部分吧。
首先要知道的是Webpack有许许多多的特性,一些是”开发模式“下才有的,一些是”生产模式“下才有的,还有一些是两种模式下都有的。
通常使用到Webpack如此多特性的项目都会有两个比较大的Webpack配置文件
为了生成bundles文件你可能在package.json文件加入如下的scripts项:
&scripts&: {
&build&: &webpack --config webpack.config.prod.js&,
&dev&: &webpack-dev-server&
值得注意的是,Webpack作为模块打包工具,提供两种用户交互接口:
Webpack CLI tool:默认的交互方式(已随Webpack本身安装到本地)
webpack-dev-server:一个Node.js服务器(需要开发者从npm自行安装)
Webpack CLI(有利于生产模式下打包)
这种方式可以从命令行获取参数也可以从配置文件(默认叫webpack.config.js)获取,将获取到的参数传入Webpack来打包。
当然你也可以从命令行(CLI)开始学习Webpack,以后你可能主要在生产模式下使用到它。
npm install webpack --g
npm install webpack --save
&scripts&: {
&build&: &webpack --config webpack.config.prod.js -p&,
&npm run build&
webpack-dev-server(有利于在开发模式下编译)
这是一个基于Express.js框架开发的web server,默认监听8080端口。server内部调用Webpack,这样做的好处是提供了额外的功能如热更新“Live Reload”以及热替换“Hot Module Replacement”(即HMR)。
npm install webpack-dev-server --save
$ webpack-dev-server --inline --hot
&scripts&: {
&start&: &webpack-dev-server --inline --hot&,
$ npm start
Webpack VS Webpack-dev-server选项
注意像inline和hot这些选项是Webpack-dev-server特有的,而另外的如hide-modules则是CLI模式特有的选项。
webpack-dev-server CLI选项和配置项
另外值得注意的是你可以通过以下两种方式向webpack-dev-server传入参数:
通过webpack.config.js文件的&devServer&对象
通过CLI选项
webpack-dev-server --hot --inline
devServer: {
inline: true,
我发现有时devServer配置项(hot: true 和inline: true)不生效,我更偏向使用如下的方式向CLI传递参数:
&scripts&: &webpack-dev-server --hot --inline&
注意:确定你没有同时传入hot:true和-hot
webpack-dev-server的“hot” 和 “inline”选项
“inline”选项会为入口页面添加“热加载”功能,“hot”选项则开启“热替换(Hot Module Reloading)”,即尝试重新加载组件改变的部分(而不是重新加载整个页面)。如果两个参数都传入,当资源改变时,webpack-dev-server将会先尝试HRM(即热替换),如果失败则重新加载整个入口页面。
$ webpack-dev-server
$ webpack-dev-server --inline
$ webpack-dev-server
--inline --hot
Enter配置项告诉Webpack应用的根模块或起始点在哪里,它的值可以是字符串、数组或对象。这看起来可能令人困惑,因为不同类型的值有着不同的目的。
像绝大多数app一样,倘若你的应用只有一个单一的入口,enter项的值你可以使用任意类型,最终输出的结果都是一样的。
enter:数组类型
但是,如果你想添加多个彼此不互相依赖的文件,你可以使用数组格式的值。
例如,你可能在html文件里引用了“googleAnalytics.js”文件,可以告诉Webpack将其加到bundle.js的最后。
enter:对象
现在,假设你的应用是多页面的(multi-page application)而不是SPA,有多个html文件(index.html和profile.html)。然后你通过一个对象告诉Webpack为每一个html生成一个bundle文件。
以下的配置将会生成两个js文件:indexEntry.js和profileEntry.js分别会在index.html和profile.html中被引用。
&script src=”dist/profileEntry.js”&&
//index.html
src=”dist/indexEntry.js”&&
注意:文件名取自“entry”对象的键名。
enter:混合类型
你也可以在enter对象里使用数组类型,例如下面的配置将会生成3个文件:vender.js(包含三个文件),index.js和profile.js文件。
output项告诉webpack怎样存储输出结果以及存储到哪里。output的两个配置项“path”和“publicPath”可能会造成困惑。
“path”仅仅告诉Webpack结果存储在哪里,然而“publicPath”项则被许多Webpack的插件用于在生产模式下更新内嵌到css、html文件里的url值。
例如,在localhost(译者注:即本地开发模式)里的css文件中边你可能用“./test.png”这样的url来加载图片,但是在生产模式下“test.png”文件可能会定位到CDN上并且你的Node.js服务器可能是运行在HeroKu上边的。这就意味着在生产环境你必须手动更新所有文件里的url为CDN的路径。
然而你也可以使用Webpack的“publicPath”选项和一些插件来在生产模式下编译输出文件时自动更新这些url。
background-image: url('./test.png');
background-image: url('https://someCDN/test.png');
模块加载器是可自由添加的Node模块,用于将不同类型的文件“load”或“import”并转换成浏览器可以识别的类型,如js、Stylesheet等。更高级的模块加载器甚至可以支持使用ES6里边的“require”或“import”引入模块。
例如,你可以使用babel-loader来将使用ES6语法写成的文件转换成ES5:
loaders: [{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel'
链式(管道式)的加载器(从右往左执行)
多个loader可以用在同一个文件上并且被链式调用。链式调用时从右到左执行且loader之间用“!”来分割。
例如,假设我们有一个名为“myCssFile.css”的css文件,然后我们想将它的内容使用style标签内联到最终输出的html里边。我们可以使用css-loader和style-loader两个loader来达到目的。
loaders: [{
test: /\.css$/,
loader: 'style!css'
这里展示它是如何工作的:
Webpack在模块颞部搜索在css的依赖项,即Webpack检查js文件是否有“require('myCssFile.css')”的引用,如果它发现有css的依赖,Webpack将css文件交给“css-loader”去处理
css-loader加载所有的css文件以及css自身的依赖(如,&其他css)到JSON对象里,Webpack然后将处理结果传给“style-loader”
style-loader接受JSON值然后添加一个style标签并将其内嵌到html文件里
模块加载器(loader)自身可以根据传入不同的参数进行配置。
在下面的例子中,我们可以配置url-loader来将小于1024字节的图片使用DataUrl替换而大于1024字节的图片使用url,我们可以用如下两种方式通过传入“limit“参数来实现这一目的:
babal-loader使用”presets“配置项来标识如何将ES6语法转成ES5以及如何转换React的JSX成js文件。我们可以用如下的方式使用”query“参数传入配置:
loaders: [
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel',
presets: ['react', 'es2015']
然而在很多项目里babal的配置可能比较大,因此你可以把babal-loader的配置项单独保存在一个名为”.babelrc“的文件中,在执行时babal-loader将会自动加载.babelrc文件。
所以在很多例子里,你可能会看到:
loaders: [
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel'
presets: ['react', 'es2015']
插件一般都是用于输出bundle的node模块。
例如,获取bundle.js然后压缩和混淆内容以减小文件体积。
类似的内部使用css-loader和style-loader来收集所有的css到一个地方最终将结果提取结果到一个独立的”styles.css“文件,并且在html里边引用style.css文件。
var ETP = require(&extract-text-webpack-plugin&);
loaders: [
{test: /\.css$/, loader:ETP.extract(&style-loader&,&css-loader&) }
plugins: [
new ExtractTextPlugin(&styles.css&)
注意:如果你只是想把css使用style标签内联到html里,你不必使用extract-text-webpack-plugin,仅仅使用css loader和style loader即可:
loaders: [{
test: /\.css$/,
loader: 'style!css'
你可能已经意识到了,Loader处理单独的文件级别并且通常作用于包生成之前或生成的过程中。
而插件则是处理包(bundle)或者chunk级别,且通常是bundle生成的最后阶段。一些插件如甚至更直接修改bundle的生成方式。
很多Webpack的配置文件都有一个resolve属性,然后就像下面代码所示有一个空字符串的值。空字符串在此是为了resolve一些在import文件时不带文件扩展名的表达式,如require('./myJSFile')或者import
myJSFile from './myJSFile'(译者注:实际就是自动添加后缀,默认是当成js文件来查找路径)
resolve: {
extensions: ['', '.js', '.jsx']
就这么多。
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:105958次
积分:1308
积分:1308
排名:千里之外
原创:26篇
评论:13条
文章:14篇
阅读:48555
(2)(2)(2)(2)(7)(9)(1)(7)
(window.slotbydup = window.slotbydup || []).push({
id: '4740887',
container: s,
size: '250,250',
display: 'inlay-fix'在 SegmentFault,解决技术问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。
一线的工程师、著名开源项目的作者们,都在这里:
获取验证码
已有账号?
问题对人有帮助,内容完整,我也想知道答案
问题没有实际价值,缺少关键内容,没有改进余地
最近在看webpack的相关知识,想要把它运用到项目中。 原来项目用的是requirejs,属于AMD,异步加载模块,我对amd的理解是 在浏览器上,浏览器加载编译器(requirejs),然后根据依赖加载其他模块,这个过程是在浏览器上完成的。至于异步是指script中引用requirejs,下面还可以写其他script。下面引入的js文件可能会比上面文件很快加载。
至于webpack是在服务器端(本地)根据依赖把js文件打包好。也就是说把依赖处理的工作放在了服务器端。最后在js上直接引入就可以了。
不知道我这样的理解对不对?另外我看到webpack兼容AMD,但是它本身是在浏览器中是同步或是异步的呢?
希望和大家交流加深理解
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
浏览器不执行什么,兼容AMD写法,也是在服务器打包好的。你可以看一下webpack build出来的文件长啥样,就是把html,css,js模块,都放到一个js里,你有一个入口html,调用这个js。没那么复杂
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
我理解的 webpack 是这样的,它打包之后,实际上生成的是一个普普通通的 js 文件而已,并没有你想象的辣么神奇。
它模块化的原理大概是下面这样的:
var a = {};
(function(a){
a.module1 = '';
(function(a){
//引入模块1
var module1 = a.module1;
a.module2 = '';
分享到微博?
关闭理由:
删除理由:
忽略理由:
推广(招聘、广告、SEO 等)方面的内容
与已有问题重复(请编辑该提问指向已有相同问题)
答非所问,不符合答题要求
宜作评论而非答案
带有人身攻击、辱骂、仇恨等违反条款的内容
无法获得确切结果的问题
非开发直接相关的问题
非技术提问的讨论型问题
其他原因(请补充说明)
我要该,理由是:webpack执行机制流程是怎么样的? - 知乎222被浏览5950分享邀请回答lihuanghe.github.io/2016/05/30/webpack-source-analyse.html256 条评论分享收藏感谢收起5添加评论分享收藏感谢收起查看更多回答彻底解决Webpack打包性能问题 - 知乎专栏
{"debug":false,"apiRoot":"","paySDK":"/api/js","wechatConfigAPI":"/api/wechat/jssdkconfig","name":"production","instance":"column","tokens":{"X-XSRF-TOKEN":null,"X-UDID":null,"Authorization":"oauth c3cef7c66aa9e6a1e3160e20"}}
{"database":{"Post":{"":{"contributes":[{"sourceColumn":{"lastUpdated":,"description":"","permission":"COLUMN_PUBLIC","memberId":1360792,"contributePermission":"COLUMN_PUBLIC","translatedCommentPermission":"all","canManage":true,"intro":"简介 is not defined","urlToken":"starkwang","id":11622,"imagePath":"faead8d536.jpeg","slug":"starkwang","applyReason":"0","name":"一只码农的技术日记","title":"一只码农的技术日记","url":"/starkwang","commentPermission":"COLUMN_ALL_CAN_COMMENT","canPost":true,"created":,"state":"COLUMN_NORMAL","followers":4808,"avatar":{"id":"faead8d536","template":"/{id}_{size}.jpeg"},"activateAuthorRequested":false,"following":false,"imageUrl":"/faead8d536_l.jpeg","articlesCount":25},"state":"accepted","targetPost":{"titleImage":"/897c2df2f21aa6b2f9f4a_r.png","lastUpdated":,"imagePath":"897c2df2f21aa6b2f9f4a.png","permission":"ARTICLE_PUBLIC","topics":[,225],"summary":"这几天写腾讯实习生 Mini 项目的时候用上了 react 全家桶,当然同时引入了 Webpack 作为打包工具。但是开发过程中遇到一个很棘手的问题就是,react 加上 react-router、material-ui、superagent、eventproxy 这些第三方轮子一共有好几百个 module,Webpack …","copyPermission":"ARTICLE_COPYABLE","translatedCommentPermission":"all","likes":0,"origAuthorId":0,"publishedTime":"T14:51:58+08:00","sourceUrl":"","urlToken":,"id":883604,"withContent":false,"slug":,"bigTitleImage":false,"title":"彻底解决Webpack打包性能问题","url":"/p/","commentPermission":"ARTICLE_ALL_CAN_COMMENT","snapshotUrl":"","created":,"comments":0,"columnId":11622,"content":"","parentId":0,"state":"ARTICLE_PUBLISHED","imageUrl":"/897c2df2f21aa6b2f9f4a_r.png","author":{"bio":"企鹅培育与饲养","isFollowing":false,"hash":"d965f32ae58ad3a48a1585a4","uid":68,"isOrg":false,"slug":"starkwei","isFollowed":false,"description":"/starkwang","name":"Stark伟","profileUrl":"/people/starkwei","avatar":{"id":"v2-cfafbe3e362ecf37f1d5fcb7f488f13c","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},"memberId":1360792,"excerptTitle":"","voteType":"ARTICLE_VOTE_CLEAR"},"id":402137}],"title":"彻底解决Webpack打包性能问题","author":"starkwei","content":"这几天写腾讯实习生 Mini 项目的时候用上了 react 全家桶,当然同时引入了 Webpack 作为打包工具。但是开发过程中遇到一个很棘手的问题就是,react 加上 react-router、material-ui、superagent、eventproxy 这些第三方轮子一共有好几百个 module,Webpack 的打包速度极慢。这对于开发是非常不好的体验,同时效率也极低。问题分析我们先来看一下完全没有任何优化的时候,Webpack 的打包速度(使用了jsx和babel的loader)。下面是我们的测试文件://test.js\nvar react = require('react');\nvar ReactAddonsCssTransitionGroup = require('react-addons-css-transition-group');\nvar reactDOM = require('react-dom');\nvar reactRouter = require('react-router');\nvar superagent = require(\"superagent\");\nvar eventproxy = require(\"eventproxy\");\n运行webpack test.js\n在我的2015款RMBP13,i5处理器,全SSD下,性能是这样的:没错你没有看错,这几个第三方轮子加起来有整整668个模块,全部打包需要20多秒。这意味着什么呢?你每次对业务代码的修改,gulp 或者 Webpack 监测到后都会重新打包,你要足足等20秒才能看到自己的修改结果。但是需要重新打包的只有你的业务代码,这些第三方库是完全不用重新打包的,它们的存在只会拖累打包性能。所以我们要找一些方法来优化这个过程。配置externalsWebpack 可以配置 externals 来将依赖的库指向全局变量,从而不再打包这个库,比如对于这样一个文件:import React from 'react';\nconsole.log(React);\n如果你在 Webpack.config.js 中配置了externals:module.exports = {\n
externals: {\n
'react': 'window.React'\n
//其它配置忽略...... \n};\n等于让 Webpack 知道,对于 react 这个模块就不要打包啦,直接指向 window.React 就好。不过别忘了加载 react.min.js,让全局中有 React 这个变量。我们来看看性能,因为不用打包 React 了所以速度自然很快,包也很小:配置externals的缺陷问题如果就这么简单地解决了的话,那我就没必要写这篇文章了,下面我们加一个 react 的动画库 react-addons-css-transition-group 来试一试:import React from 'react';\nimport ReactAddonsCssTransitionGroup from 'react-addons-css-transition-group';\nconsole.log(React);\n对,你没有看错,我也没有截错图,新加了一个很小很小的动画库之后,性能又爆炸了。从模块数来看,一定是 Webpack 又把 react 重新打包了一遍。我们来看一下为什么一个很小很小的动画库会导致 Webpack 又傻傻地把 react 重新打包了一遍。找到 react-addons-css-transition-group 这个模块,然后看看它是怎么写的:// react-addons-css-transition-group模块\n// 入口文件 index.js\nmodule.exports = require('react/lib/ReactCSSTransitionGroup');\n这个动画模块就只有一行代码,唯一的作用就是指向 react 下面的一个子模块,我们再来看看这个子模块是怎么写的:// react模块\n// react/lib/ReactCSSTransitionGroup.js\nvar React = require('./React');\nvar ReactTransitionGroup = require('./ReactTransitionGroup');\nvar ReactCSSTransitionGroupChild = require('./ReactCSSTransitionGroupChild');\n//....剩余代码忽略\n这个子模块又反回去依赖了 react 整个库的入口,这就是拖累 Webpack 的罪魁祸首。总而言之,问题是这样产生的:Webpack 发现我们依赖了 react-addons-css-transition-group; Webpack 去打包 react-addons-css-transition-group 的时候发现它依赖了 react 模块下的一个叫 ReactTransitionGroup.js 的文件,于是 Webpack 去打包这个文件;ReactTransitionGroup.js 依赖了整个 react 的入口文件 React.js,虽然我们设置了 externals ,但是 Webpack 不知道这个入口文件等效于 react 模块本身,于是我们可爱又敬业的 Webpack 就把整个 react 又重新打包了一遍。读到这里你可能会有疑问,为什么不能把这个动画库也设置到 externals 里,这样不是就不用打包了吗?问题就在于,这个动画库并没有提供生产环境的文件,或者说这个库根本没有提供 react-addons-css-transition-group.min.js 这个文件。这个问题不只存在于 react-addons-css-transition-group 中,对于 react 的大多数现有库来说都有这个依赖关系复杂的问题。初级解决方法所以对于这个问题的解决方法就是,手工打包这些 module,然后设置 externals ,让 Webpack 不再打包它们。我们需要这样一个 lib-bundle.js 文件:window.__LIB[\"react\"] = require(\"react\");\nwindow.__LIB[\"react-addons-css-transition-group\"] = require(\"react-addons-css-transition-group\");\n// ...其它依赖包\n我们在这里把一些第三方库注册到了 window.__LIB 下,这些库可以作为底层的基础库,免于重复打包。然后执行 webpack lib-bundle.js lib.js\n得到打包好的 lib.js。然后去设置我们的 externals :var webpack = require('webpack');\nmodule.exports = {\nexternals: {\n
'react': 'window.__LIB[\"react\"]',\n
'react-addons-css-transition-group': 'window.__LIB[\"react-addons-css-transition-group\"]',\n
// 其它库\n
//其它配置忽略...... \n};\n这时由于 externals 的存在,Webpack 打包的时候就会避开这些模块超多,依赖关系复杂的库,把这些第三方 module 的入口指向预先打包好的 lib.js 的入口 window.__LIB,从而只打包我们的业务代码。终极解决方法上面我们提到的方法本质上就是一种“动态链接库(dll)”的思想,这在 windows 系统下面是一种很常见的思想。一个 dll 包,就是一个很纯净的依赖库,它本身不能运行,是用来给你的 app 或者业务代码引用的。同样的 Webpack 最近也新加入了这个功能:webpack.DllPlugin。使用这个功能需要把打包过程分成两步: 打包ddl包 引用ddl包,打包业务代码首先我们来打包ddl包,首先配置一个这样的 ddl.config.js:const webpack = require('webpack');\n\nconst vendors = [\n
'react',\n
'react-dom',\n
'react-router',\n
// ...其它库\n];\n\nmodule.exports = {\n
output: {\n
path: 'build',\n
filename: '[name].js',\n
library: '[name]',\n
entry: {\n
\"lib\": vendors,\n
plugins: [\n
new webpack.DllPlugin({\n
path: 'manifest.json',\n
name: '[name]',\n
context: __dirname,\n
],\n};\nwebpack.DllPlugin 的选项中:path 是 manifest.json 文件的输出路径,这个文件会用于后续的业务代码打包;name 是 dll 暴露的对象名,要跟 output.library 保持一致;context 是解析包路径的上下文,这个要跟接下来配置的 webpack.config.js 一致。运行Webpack,会输出两个文件一个是打包好的 lib.js,一个就是 manifest.json,它里面的内容大概是这样的:{\n
\"name\": \"vendor_ac51ba426d4f259b8b18\",\n
\"content\": {\n
\"./node_modules/react/react.js\": 1,\n
\"./node_modules/react/lib/React.js\": 2,\n
\"./node_modules/react/node_modules/object-assign/index.js\": 3,\n
\"./node_modules/react/lib/ReactChildren.js\": 4,\n
\"./node_modules/react/lib/PooledClass.js\": 5,\n
\"./node_modules/react/lib/reactProdInvariant.js\": 6,\n
// ............\n
}\n}\n接下来我们就可以快乐地打包业务代码啦,首先写好打包配置文件 webpack.config.js:const webpack = require('webpack');\nmodule.exports = {\n
output: {\n
path: 'build',\n
filename: '[name].js',\n
entry: {\n
app: './src/index.js',\n
plugins: [\n
new webpack.DllReferencePlugin({\n
context: __dirname,\n
manifest: require('./manifest.json'),\n
],\n};\nwebpack.DllReferencePlugin 的选项中:context 需要跟之前保持一致,这个用来指导 Webpack 匹配 manifest.json 中库的路径;manifest 用来引入刚才输出的 manifest.json 文件。DllPlugin 本质上的做法和我们手动分离这些第三方库是一样的,但是对于包极多的应用来说,自动化明显加快了生产效率。","updated":"T06:51:58.000Z","canComment":false,"commentPermission":"anyone","commentCount":59,"collapsedCount":0,"likeCount":334,"state":"published","isLiked":false,"slug":"","lastestTipjarors":[],"isTitleImageFullScreen":false,"rating":"none","titleImage":"/897c2df2f21aa6b2f9f4a_r.png","links":{"comments":"/api/posts//comments"},"reviewers":[],"topics":[{"url":"/topic/","id":"","name":"webpack"},{"url":"/topic/","id":"","name":"JavaScript"},{"url":"/topic/","id":"","name":"前端开发"}],"adminClosedComment":false,"titleImageSize":{"width":1920,"height":960},"href":"/api/posts/","excerptTitle":"","column":{"slug":"starkwang","name":"一只码农的技术日记"},"tipjarState":"activated","tipjarTagLine":"谢谢OvO","sourceUrl":"","pageCommentsCount":59,"tipjarorCount":0,"annotationAction":[],"hasPublishingDraft":false,"snapshotUrl":"","publishedTime":"T14:51:58+08:00","url":"/p/","lastestLikers":[{"bio":null,"isFollowing":false,"hash":"35a6bc101e","uid":64,"isOrg":false,"slug":"yu-mei-zhi-zhu","isFollowed":false,"description":"","name":"愚昧之主","profileUrl":"/people/yu-mei-zhi-zhu","avatar":{"id":"6f5efbe6b","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},{"bio":"","isFollowing":false,"hash":"7e4da99b0fcd09a7e0a72c8","uid":92,"isOrg":false,"slug":"ke-jie-cheng","isFollowed":false,"description":"","name":"木君","profileUrl":"/people/ke-jie-cheng","avatar":{"id":"fedab7d7ec18f4cdfe1a0d16076cb61d","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},{"bio":"Stay Hungry,Stay Foolish","isFollowing":false,"hash":"faa985c0a0d98f96896c","uid":36,"isOrg":false,"slug":"kidney","isFollowed":false,"description":"","name":"kidney","profileUrl":"/people/kidney","avatar":{"id":"","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},{"bio":"IT","isFollowing":false,"hash":"2f368a737b31bda78fefeb8","uid":553500,"isOrg":false,"slug":"jason-code-40-98","isFollowed":false,"description":"常与同好争高下,不与傻瓜论短长!","name":"code先生","profileUrl":"/people/jason-code-40-98","avatar":{"id":"da8e974dc","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},{"bio":"少刷知乎多读书","isFollowing":false,"hash":"e12fe03fbf0","uid":32,"isOrg":false,"slug":"timmy-king","isFollowed":false,"description":"偶尔写诗的程序员","name":"Timmy King","profileUrl":"/people/timmy-king","avatar":{"id":"2aa578818","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false}],"summary":"这几天写腾讯实习生 Mini 项目的时候用上了 react 全家桶,当然同时引入了 Webpack 作为打包工具。但是开发过程中遇到一个很棘手的问题就是,react 加上 react-router、material-ui、superagent、eventproxy 这些第三方轮子一共有好几百个 module,Webpack …","reviewingCommentsCount":0,"meta":{"previous":{"isTitleImageFullScreen":false,"rating":"none","titleImage":"/50/a48eedb38a417c57a3171aec1d10dd0b_xl.jpg","links":{"comments":"/api/posts//comments"},"topics":[{"url":"/topic/","id":"","name":"函数式编程"},{"url":"/topic/","id":"","name":"JavaScript"}],"adminClosedComment":false,"href":"/api/posts/","excerptTitle":"","author":{"bio":"企鹅培育与饲养","isFollowing":false,"hash":"d965f32ae58ad3a48a1585a4","uid":68,"isOrg":false,"slug":"starkwei","isFollowed":false,"description":"/starkwang","name":"Stark伟","profileUrl":"/people/starkwei","avatar":{"id":"v2-cfafbe3e362ecf37f1d5fcb7f488f13c","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},"column":{"slug":"starkwang","name":"一只码农的技术日记"},"content":"一、引言说到函数式编程,大家可能第一印象都是学院派的那些晦涩难懂的代码,充满了一大堆抽象的不知所云的符号,似乎只有大学里的计算机教授才会使用这些东西。在曾经的某个时代可能确实如此,但是近年来随着技术的发展,函数式编程已经在实际生产中发挥巨大的作用了,越来越多的语言开始加入闭包,匿名函数等非常典型的函数式编程的特性,从某种程度上来讲,函数式编程正在逐步“同化”命令式编程。JavaScript 作为一种典型的多范式编程语言,这两年随着React的火热,函数式编程的概念也开始流行起来,RxJS、cycleJS、lodashJS、underscoreJS等多种开源库都使用了函数式的特性。所以下面介绍一些函数式编程的知识和概念。二、纯函数如果你还记得一些初中的数学知识的话,函数 f 的概念就是,对于输入 x 产生一个输出 y = f(x)。这便是一种最简单的纯函数。纯函数的定义是,对于相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用,也不依赖外部环境的状态。下面来举个栗子,比如在Javascript中对于数组的操作,有些是纯的,有些就不是纯的:var arr = [1,2,3,4,5];\n\n// Array.slice是纯函数,因为它没有副作用,对于固定的输入,输出总是固定的\n// 可以,这很函数式\nxs.slice(0,3);\n//=& [1,2,3]\nxs.slice(0,3);\n//=& [1,2,3]\n\n// Array.splice是不纯的,它有副作用,对于固定的输入,输出不是固定的\n// 这不函数式\nxs.splice(0,3);\n//=& [1,2,3]\nxs.splice(0,3);\n//=& [4,5]\nxs.splice(0,3);\n//=& []\n在函数式编程中,我们想要的是 slice 这样的纯函数,而不是 splice这种每次调用后都会把数据弄得一团乱的函数。为什么函数式编程会排斥不纯的函数呢?下面再看一个例子://不纯的\nvar min = 18;\nvar checkage = age =& age & min;\n\n//纯的,这很函数式\nvar checkage = age =& age & 18;\n在不纯的版本中,checkage 这个函数的行为不仅取决于输入的参数 age,还取决于一个外部的变量 min,换句话说,这个函数的行为需要由外部的系统环境决定。对于大型系统来说,这种对于外部状态的依赖是造成系统复杂性大大提高的主要原因。可以注意到,纯的 checkage 把关键数字 18 硬编码在函数内部,扩展性比较差,我们可以在后面的柯里化中看到如何用优雅的函数式解决这种问题。纯函数不仅可以有效降低系统的复杂度,还有很多很棒的特性,比如可缓存性:import _ from 'lodash';\nvar sin = _.memorize(x =& Math.sin(x));\n\n//第一次计算的时候会稍慢一点\nvar a = sin(1);\n\n//第二次有了缓存,速度极快\nvar b = sin(1);\n三、函数的柯里化函数柯里化(curry)的定义很简单:传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。比如对于加法函数 var add = (x, y) =& x + y ,我们可以这样进行柯里化://比较容易读懂的ES5写法\nvar add = function(x){\n
return function(y){\n
return x + y\n
}\n}\n\n//ES6写法,也是比较正统的函数式写法\nvar add = x =& (y =& x + y);\n\n//试试看\nvar add2 = add(2);\nvar add200 = add(200);\n\nadd2(2); // =&4\nadd200(50); // =&250\n对于加法这种极其简单的函数来说,柯里化并没有什么大用处。还记得上面那个 checkage 的函数吗?我们可以这样柯里化它:var checkage = min =& (age =& age & min);\nvar checkage18 = checkage(18);\ncheckage18(20);\n// =&true\n事实上柯里化是一种“预加载”函数的方法,通过传递较少的参数,得到一个已经记住了这些参数的新函数,某种意义上讲,这是一种对参数的“缓存”,是一种非常高效的编写函数的方法:import { curry } from 'lodash';\n\n//首先柯里化两个纯函数\nvar match = curry((reg, str) =& str.match(reg));\nvar filter = curry((f, arr) =& arr.filter(f));\n\n//判断字符串里有没有空格\nvar haveSpace = match(/\\s+/g);\n\nhaveSpace(\"ffffffff\");\n//=&null\n\nhaveSpace(\"a b\");\n//=&[\" \"]\n\nfilter(haveSpace, [\"abcdefg\", \"Hello World\"]);\n//=&[\"Hello world\"]\n四、函数组合学会了使用纯函数以及如何把它柯里化之后,我们会很容易写出这样的“包菜式”代码:h(g(f(x)));\n虽然这也是函数式的代码,但它依然存在某种意义上的“不优雅”。为了解决函数嵌套的问题,我们需要用到“函数组合”://两个函数的组合\nvar compose = function(f, g) {\n
return function(x) {\n
return f(g(x));\n
};\n};\n\n//或者\nvar compose = (f, g) =& (x =& f(g(x)));\n\nvar add1 = x =& x + 1;\nvar mul5 = x =& x * 5;\n\ncompose(mul5, add1)(2);\n// =&15 \n我们定义的compose就像双面胶一样,可以把任何两个纯函数结合到一起。当然你也可以扩展出组合三个函数的“三面胶”,甚至“四面胶”“N面胶”。这种灵活的组合可以让我们像拼积木一样来组合函数式的代码:var first = arr =& arr[0];\nvar reverse = arr =& arr.reverse();\n\nvar last = compose(first, reverse);\n\nlast([1,2,3,4,5]);\n// =&5\n五、Point Free有了柯里化和函数组合的基础知识,下面介绍一下Point Free这种代码风格。细心的话你可能会注意到,之前的代码中我们总是喜欢把一些对象自带的方法转化成纯函数:var map = (f, arr) =& arr.map(f);\n\nvar toUpperCase = word =& word.toUpperCase();\n这种做法是有原因的。Point Free这种模式现在还暂且没有中文的翻译,有兴趣的话可以看看这里的英文解释:用中文解释的话大概就是,不要命名转瞬即逝的中间变量,比如://这不Piont free\nvar f = str =& str.toUpperCase().split(' ');\n这个函数中,我们使用了 str 作为我们的中间变量,但这个中间变量除了让代码变得长了一点以外是毫无意义的。下面改造一下这段代码:var toUpperCase = word =& word.toUpperCase();\nvar split = x =& (str =& str.split(x));\n\nvar f = compose(split(' '), toUpperCase);\n\nf(\"abcd efgh\");\n// =&[\"ABCD\", \"EFGH\"]\n这种风格能够帮助我们减少不必要的命名,让代码保持简洁和通用。当然,为了在一些函数中写出Point Free的风格,在代码的其它地方必然是不那么Point Free的,这个地方需要自己取舍。六、声明式与命令式代码命令式代码的意思就是,我们通过编写一条又一条指令去让计算机执行一些动作,这其中一般都会涉及到很多繁杂的细节。而声明式就要优雅很多了,我们通过写表达式的方式来声明我们想干什么,而不是通过一步一步的指示。//命令式\nvar CEOs = [];\nfor(var i = 0; i & companies.length; i++){\n
CEOs.push(companies[i].CEO)\n}\n\n//声明式\nvar CEOs = companies.map(c =& c.CEO);\n命令式的写法要先实例化一个数组,然后再对 companies 数组进行for循环遍历,手动命名、判断、增加计数器,就好像你开了一辆零件全部暴露在外的汽车一样,虽然很机械朋克风,但这并不是优雅的程序员应该做的。声明式的写法是一个表达式,如何进行计数器迭代,返回的数组如何收集,这些细节都隐藏了起来。它指明的是做什么,而不是怎么做。除了更加清晰和简洁之外,map 函数还可以进一步独立优化,甚至用解释器内置的速度极快的 map 函数,这么一来我们主要的业务代码就无须改动了。函数式编程的一个明显的好处就是这种声明式的代码,对于无副作用的纯函数,我们完全可以不考虑函数内部是如何实现的,专注于编写业务代码。优化代码时,目光只需要集中在这些稳定坚固的函数内部即可。相反,不纯的不函数式的代码会产生副作用或者依赖外部系统环境,使用它们的时候总是要考虑这些不干净的副作用。在复杂的系统中,这对于程序员的心智来说是极大的负担。七、尾声任何代码都是要有实际用处才有意义,对于JS来说也是如此。然而现实的编程世界显然不如范例中的函数式世界那么美好,实际应用中的JS是要接触到ajax、DOM操作,NodeJS环境中读写文件、网络操作这些对于外部环境强依赖,有明显副作用的“很脏”的工作。这对于函数式编程来说也是很大的挑战,所以我们也需要更强大的技术去解决这些“脏问题”。我会在下一篇文章中介绍函数式编程的更加高阶一些的知识,例如Functor、Monad等等概念。八、参考1、2、3、《JavaScript函数式编程》【美】迈克尔·佛格斯","state":"published","sourceUrl":"","pageCommentsCount":0,"canComment":false,"snapshotUrl":"","slug":,"publishedTime":"T14:37:41+08:00","url":"/p/","title":"JavaScript函数式编程(一)","summary":"一、引言 说到函数式编程,大家可能第一印象都是学院派的那些晦涩难懂的代码,充满了一大堆抽象的不知所云的符号,似乎只有大学里的计算机教授才会使用这些东西。在曾经的某个时代可能确实如此,但是近年来随着技术的发展,函数式编程已经在实际生产中发挥…","reviewingCommentsCount":0,"meta":{"previous":null,"next":null},"commentPermission":"anyone","commentsCount":36,"likesCount":391},"next":{"isTitleImageFullScreen":false,"rating":"none","titleImage":"","links":{"comments":"/api/posts//comments"},"topics":[{"url":"/topic/","id":"","name":"JavaScript"}],"adminClosedComment":false,"href":"/api/posts/","excerptTitle":"","author":{"bio":"企鹅培育与饲养","isFollowing":false,"hash":"d965f32ae58ad3a48a1585a4","uid":68,"isOrg":false,"slug":"starkwei","isFollowed":false,"description":"/starkwang","name":"Stark伟","profileUrl":"/people/starkwei","avatar":{"id":"v2-cfafbe3e362ecf37f1d5fcb7f488f13c","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false},"content":"哈哈哈哈Mini项目拿金奖啦拿金奖咯~一个多星期前其他组在组队的时候都不怎么欢迎前端,侧重做APP,前端最多弄个宣传页H5啥的。然而他们不知道的是,javascript这种真·全平台的语言极其适合这种这种7天的快速开发。我要是PM一定组12个前端,6个用node写服务器,6个用react写用户端(还可以顺便把react-native做了)答辩的时候评委里碰巧有个前端T3,她也是难得看到一个侧重前端技术的组(不知道是不是唯一一个)。我们用react、react-router做的SPA当然有很多地方可以和她谈笑风生啦,从技术选型说到react的组件状态管理再说到react服务器端直出渲染,顺便说了说前几天发的那篇关于优化Webpack打包速度的文章。看她最后的表情我能确定起码这个评委被搞定了(^?^)ノ当然队友们也很给力,服务器端架构简直让评委无黑点,安卓同学一个人通了几天宵单挑APP,PM的用户调研、竞品分析也是所有组最棒的,测试同学做的压力测试和安卓帧率测试的数据也非常详尽。哈哈总之就是感谢腾讯,感谢SNG,也感谢这群靠谱的队友~","state":"published","sourceUrl":"","pageCommentsCount":0,"canComment":false,"snapshotUrl":"","slug":,"publishedTime":"T21:03:22+08:00","url":"/p/","title":"得瑟一下","summary":"哈哈哈哈Mini项目拿金奖啦拿金奖咯~ 一个多星期前其他组在组队的时候都不怎么欢迎前端,侧重做APP,前端最多弄个宣传页H5啥的。 然而他们不知道的是,javascript这种真·全平台的语言极其适合这种这种7天的快速开发。我要是PM一定组12个前端,6个用node写…","reviewingCommentsCount":0,"meta":{"previous":null,"next":null},"commentPermission":"anyone","commentsCount":8,"likesCount":30}},"annotationDetail":null,"commentsCount":59,"likesCount":334,"FULLINFO":true}},"User":{"starkwei":{"isFollowed":false,"name":"Stark伟","headline":"/starkwang","avatarUrl":"/v2-cfafbe3e362ecf37f1d5fcb7f488f13c_s.jpg","isFollowing":false,"type":"people","slug":"starkwei","bio":"企鹅培育与饲养","hash":"d965f32ae58ad3a48a1585a4","uid":68,"isOrg":false,"description":"/starkwang","profileUrl":"/people/starkwei","avatar":{"id":"v2-cfafbe3e362ecf37f1d5fcb7f488f13c","template":"/{id}_{size}.jpg"},"isOrgWhiteList":false,"badge":{"identity":null,"bestAnswerer":null}}},"Comment":{},"favlists":{}},"me":{},"global":{},"columns":{"next":{},"starkwang":{"following":false,"canManage":false,"href":"/api/columns/starkwang","name":"一只码农的技术日记","creator":{"slug":"starkwei"},"url":"/starkwang","slug":"starkwang","avatar":{"id":"faead8d536","template":"/{id}_{size}.jpeg"}}},"columnPosts":{},"columnSettings":{"colomnAuthor":[],"uploadAvatarDetails":"","contributeRequests":[],"contributeRequestsTotalCount":0,"inviteAuthor":""},"postComments":{},"postReviewComments":{"comments":[],"newComments":[],"hasMore":true},"favlistsByUser":{},"favlistRelations":{},"promotions":{},"switches":{"couldAddVideo":false},"draft":{"titleImage":"","titleImageSize":{},"isTitleImageFullScreen":false,"canTitleImageFullScreen":false,"title":"","titleImageUploading":false,"error":"","content":"","draftLoading":false,"globalLoading":false,"pendingVideo":{"resource":null,"error":null}},"drafts":{"draftsList":[],"next":{}},"config":{"userNotBindPhoneTipString":{}},"recommendPosts":{"articleRecommendations":[],"columnRecommendations":[]},"env":{"edition":{},"isAppView":false,"appViewConfig":{"content_padding_top":128,"content_padding_bottom":56,"content_padding_left":16,"content_padding_right":16,"title_font_size":22,"body_font_size":16,"is_dark_theme":false,"can_auto_load_image":true,"app_info":"OS=iOS"},"isApp":false},"sys":{},"message":{"newCount":0},"pushNotification":{"newCount":0}}}

我要回帖

更多关于 webpack开启服务器 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信