前端记录

记录遇到的一些问题和解决方法,和一些技巧。


Sublime

快速执行任务

按住 Shift+Command+P 可以弹出一个输入框,可以输入关键字快捷执行一些操作。

例如美化 HTML 格式插件的功能,输入 HTML,会智能匹配结果,选中 HTMLPrettify 即可实现该插件的操作。

输入 package control 快速进入安装 plugin 界面。

Webpack

遇到的坑

babel 的 resolve.alias 问题

因为看到通过配置 alias 可以加快 webpack 的执行速度,所以尝试了一下使用,但没想到遇到了挺多问题,最后也没解决,这里记录一下。

首先在 webpace.config.js 新添加的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

...

var node_modules_dir = path.resolve(__dirname, 'node_modules');
var pathToReact = path.resolve(node_modules_dir, 'react/dist/react.min.js');

var config = {

...

resolve: {
alias: {
'react': pathToReact
}
},
module: {
noParse: [pathToReact],
...
}
...

基本跟照着官网的文档来的。

第一个问题是 alias 配置问题,pathToReact 打印出来的路径是 /Users/yang/Desktop/Web/公司/cmw/reactUI/node_modules/react/dist/react.min.js,在使用 require(‘react’) 成功指向了 react/dist/react.min.js

但使用 react-addons-css-transition-group 时 (通过 npm 安装,0.14 版 react 将这个包分出来),包里面的 index.js 内容是

module.exports = require('react/lib/ReactCSSTransitionGroup');

编译时 react/ 却被替换成了 react/dist/react.min.js

module.exports = require('react/dist/react.min.js/lib/ReactCSSTransitionGroup');

第二个问题,还是 alias 的问题,多番尝试后将 'react': pathToReact 改成 'react$': pathToReact,多了一个 $ 编译就通过了,但是测试网站时报一些 Invariant Violation: addComponentAsRefTo ... 之类的错误,查了一下原因可能是重复引用了 react 吧,具体是什么原因没查到。

所以还是弄不懂 alias 的规则,所以暂时使用了。而且在 babel-loader 添加一段 cacheDirectory: true,,出了第一次慢点,以后编译都挺快的。

import 和 require 冲突的问题

如果一个 module 使用 export default [ModuleName]; 导出 module,而导入时却使用 require([ModuleName]),则会报错:

1
2

Uncaught Error: Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.

解决方式是使用对应的语法:

import from 'ModuleName' 对应 export default 'ModuleName';

require ('ModuleName') 对应 module.exports = 'Modulename';

这个问题出现在使用 webpack build 时,使用了 babel 6 进行语法转换,下面是 babel-loader 的配置:

1
2
3
4
5
6
7
8
9
10

{
test: /\.js[x]?$/,
exclude: /(node_modules|bower_components)/,//排除指定文件夹
loader: 'babel-loader', // 'babel-loader' is also a legal name to reference
query: {
presets: [ 'stage-0','es2015','react'],
plugins: ['transform-object-assign']
}
},

使用项目自带的 gulp 配置没出现这个问题。

查了下资料,gulp 使用的是 babel 5,我在 webpack 使用的是 babel 6,而 babel 6 将所有东西都以 plugin 的形式分离出去了,默认是没功能的。

而仅仅 es5 就有 20多个 plugin,所以提供了 presets 让我们简化操作。

资料地址

5. babel-loader 的问题

在使用 ES 6 的 JS 代码中,用到了 Object.assgin 方法合并两个对象。使用 babel-loader 可以成功 build,但是项目运行时报找不到该对象的错。

这个问题可能是在 loader 配置 exclude,排除了 node_modules 文件夹,没有仔细去认证。

解决方式是需要另外安装 babel-loader 的 plugin。安装时也遇到了坑,回复说需要安装的是 babel-plugin-object-assign,但安装完成后进行 build 时报错。又找了一下资料,尝试安装 babel-plugin-transform-object-assign,然后 build 成功了,估计是 babel 5 和 babel 6 之间版本的问题吧。

下面是操作过程,首先安装 plugin:

1
2

npm i babel-plugin-transform-object-assign --save-dev

然后应用到 webpack.config.js 中:

1
2
3
4
5
6
7
8
9

{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/, //排除指定文件夹
loader: 'babel-loader', // 'babel-loader' is also a legal name to reference
query: {
presets: ['stage-0', 'es2015', 'react'],
plugins: ['transform-object-assign']
}

4.less 加载的问题

加载 less 遇到了路径解析解析错误的问题。

最开始的 loader 是跟官网一样:

1
2
3
4
5

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

这样解析路径时会出错,改为 loader: 'style!raw!less' 就没问题了,不过这样会把 css 打包到 JavaScript 中,我想让 css 文件单独分离出来,所以找到了这样的解决方式:

先安装插件

1
2

npm install extract-text-webpack-plugin --save

然后引用

1
2

var ExtractTextPlugin = require("extract-text-webpack-plugin");

修改配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

module.exports = {
entry: {
css:path.resolve(__dirname, 'src/css/app.less'),//1.添加 less 入口文件
app: path.resolve(__dirname, 'src/js/app.js'),
vendors: COMMON_PACKAGES,

},
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name].js',
chunkFilename: "[id].js"
},
module: {
loaders: [{
test: /\.js[x]?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader', // 'babel-loader' is also a legal name to reference
query: {
presets: ['es2015', 'stage-0', 'react'],
}
},
// Extract css files
{
test: /\.css$/,
exclude: /(node_modules)/,
loader: ExtractTextPlugin.extract("style-loader", "css-loader")
},
// Optionally extract less files
// or any other compile-to-css language
// 2.设置 less 的 loader
{
test: /\.less$/,
exclude: /(node_modules)/,
loader: ExtractTextPlugin.extract("style-loader", "raw-loader!less-loader")
}

// You could also use other loaders the same way. I. e. the autoprefixer-loader
]
},
//3.设置 css 文件的输出名
plugins: [
new webpack.optimize.CommonsChunkPlugin('vendors', 'vendors.js'),
new ExtractTextPlugin("[name].css")
]

};

注意 less 的 loader 的 extract 使用了 raw-loader,这样可以解析路径时出错的问题,需要另外安装 raw-loader:

npm i raw-loader --save-dev

3.babel-loader 加载 JavaScript 版本问题

接第二个问题,实验项目成功后,开始在实际项目使用,结果又报错,无法识别这样的语法:

1
2
3
4
5

leftAction: () =>
{
{ app.transitionTo(props.popTo, { transition: 'reveal-from-right' , viewProps:{...props.provProps}}) };
},

理所当然的认为是无法识别 ... 的语法。于是在其它项目试验了一下 <Accordion {...props}></Accordion > 这样的语法,是可以的,并且自己项目中也支持这样的语法。在仔细看了,原来是不支持这样的语法:

1
2

viewProps:{...props.provProps}

原来这种语法是 ES7 的语法,还需要装一个 Babel 插件:

1
2

npm install babel-preset-stage-0 --save-dev
1
2
3
4
5
6
7
8
9
10
11
12
13

//webpack.config:

loaders: [
{
test: /\.js[x]?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader', // 'babel-loader' is also a legal name to reference
query: {
presets: ['es2015', 'stage-0', 'react'],
}
}
]

2.loader 问题

使用 babel 加载 jsx 时遇到无法识别 import 的问题

1
2
3
4
5

Unexpected token
You may need an appropriate loader to handle this file type.
| import React from 'react';
| import ReactDOM from 'react-dom';

开始自己配置时就出现这个问题,一摸一样的 webpack 配置其他人的项目缺没这个问题。

后来在 bable-loader 的主页看到需要传递一些参数:

1
2
3
4
5
6

{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel?presets[]=react,presets[]=es2015' // 'babel-loader' is also a legal name to reference
}

这样就问题了,估计版本跟其它项目不一致。

1.webpack-dev-server build 问题

webpack-dev-server 在运行时 build 的文件会生成到内存中,在 finder 中看不到文件的变化。
找了很久都找不到 build 的文件在哪,但是效果又是可以看到的。

Git

一次问题记录

可以使用 revert 回滚之前的 commit,回滚之后工作区的内容已经变了,无法像提交之前可以在暂存区取消单个文件的修改。
可以点击 undo,取消上一次提交。上一次提交就是刚才回滚的提交。

JavaScript

=>

简化 function

1
2
3
4
5
6
7
8
9
10
11

var a = [
"Hydrogen",
"Helium",
"Lithium",
"Beryl­lium"
];

var a2 = a.map(function(s){ return s.length });

var a3 = a.map( s => s.length );

Lexical this

出了 arrow function,每个新 function 都定义了它自己的 this 值 (例子中构造的新对象,在 strict mode 中未定义的 function 调用,context 对象中如果 function 作为 “object method” 调用等)。这是恼人的面向对象的编程风格。

1
2
3
4
5
6
7
8
9
10
11
12
13
14

function Person() {
// The Person() constructor defines `this` as an instance of itself.
this.age = 0;

setInterval(function growUp() {
// In non-strict mode, the growUp() function defines `this`
// as the global object, which is different from the `this`
// defined by the Person() constructor.
this.age++;
}, 1000);
}

var p = new Person();

在 ECMAScript 3/5,这个问题的修复方式是通过分配 this 值给变量。

1
2
3
4
5
6
7
8
9
10
11
12

function Person() {
var self = this; // Some choose `that` instead of `self`.
// Choose one and be consistent.
self.age = 0;

setInterval(function growUp() {
// The callback refers to the `self` variable of which
// the value is the expected object.
self.age++;
}, 1000);
}

此外,可以使用 bound function (.bind() 方式) 创建 function 使 this 值正确传递到 growUp() function。

而 Arrow function 可以捕获 enclosing context 的 this 值,因此下面的代码可以预期工作:

1
2
3
4
5
6
7
8
9
10

function Person(){
this.age = 0;

setInterval(() => {
this.age++; // |this| properly refers to the person object
}, 1000);
}

var p = new Person();

现在这个方法浏览器支持得不多,了解一下就行,还有几种用法。

mozilla

bind 和 apply 和 call

bind 用来明确指定方法 (或对象) 内的 this 值。

在方法后面使用 bind 会返回一个新方法,这个方法的参数会被 bind 绑定,返回后的新方法即便重新传入参数也无效。

这里有一个原始方法:

1
2
3
4

function multiplication(a,b){
console.log(a*b);
}

使用 bind 创建一个方法:

1
2

var multiby2 = multiplication.bind(this,2);

现在调用 multiby2(b) 就与调用 multiplication(2,b) 的值相同,参数 a 已经被 bind 绑定。

通过 bind 创建的方法类型是 function b(),原始的方法会被隐藏。

而 call 和 apply:

1
2
3
4

magicMultiplication.call(this,3,2); //6

magicMultiplication.apply(this,[5,2]); //10

会立即执行方法,apply 可以以数组方法传递参数。

stackoverflow

React

好的做法

通过对组件设置一个属性 ref,即可通过 this.refs.componentsName 访问该组件的内置方法。例如使轮播组件暂停滚动(取消定时器)。

遇过的坑

调用四个相同的组件,把父组件不同 state 传递给四个组件的 visible 值,更改父组件其中一个 state,发现四个组件都触发了 render。


return 的换行 bug

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

//这样写无效,返回空白
return
<div className='OffCanvas-Position'>
<ShadowLayer visible={ true } onClick={ this.props.Close }/>
<div className={ classnames('OffCanvas-Content',this.props.Position) } >
{ this.props.children }
</div>
</div>;


//这样写才返回正常内容
return <div className='OffCanvas-Position'>
<ShadowLayer visible={ true } onClick={ this.props.Close }/>
<div className={ classnames('OffCanvas-Content',this.props.Position) } >
{ this.props.children }
</div>
</div>;


componentWillUnmount 只有在整个组件都删除时才会触发,即父组件中完全不渲染子组件。假如子组件的 render 是根据父组件的属性判断是返回内容或 null,即使子组件的返回 null,也不会触发子组件的 componentWillUnmount


对 React 组件化理解不够深,写了两个组件,都用到了一个覆盖整个页面的遮罩层,而这个遮罩层可以分离成一个组件,这是对组件化理解不够深时遇到的坑。


December 16, 2015 09:23 补充:
不会在将来移除。因为之前使用时没有提供 transitionEnterTimeouttransitionLeaveTimeout 属性,所以报错会造成不可靠的动画效果,并且不被未来版本支持。这是是说不提供这两个参数那么在未来版本无法运行动画效果。

ReactCSSTransitionGroup 使用太过麻烦,并且可能在将来的版本移除。React 版本 0.14.3。


state 命名要以大写开头,不知道是 React 强制规定还是其它规则组件所要求。React 版本 0.14.3。


… 运算符

December 14, 2015 14:24

常用于将父组件的 props 完整传递给子组件。


暂时未知它的作用,可能跟遍历数组相关,下面是一个使用情境。

在组件的 render 中:

1
2
3
4

componentDidUpdate: function() {
React.render(<div {...this.props} className='test'>{this.props.children}</div>, this.ModalBoxElement);
},

调用组件的代码:

1
2
3
4
5
6
7

<Container scrollable={scrollable} >
<Modal IsShowModal = { this.state.ShowModal } ModalId={5} title="我没有标题" data-title2="我没有标题2">
<a> Hello i am Modal </a>
</Modal>
<input type="button" value="showModalBox" onClick={ this.OnClick } />
</Container>

生成的 HTML:

1
2

<div id="5" class="ModalBox-Show"><div title="我没有标题" data-title2="我没有标题2" class="test" data-reactid=".1"><a data-reactid=".1.0"> Hello i am Modal </a></div></div>

一旦将 ... 去掉,则出现语法错误。

ref

有时需要绕过 React 的虚拟 DOM,明确的使用原生 DOM API,例如使用 jQuery 插件时的操作,React为你提供了安全仓口来直接使用底层API。

为了与浏览器互动,你需要一个指向 DOM node 的引用。React有一个函数ReactDOM.findDOMNode(component) ,你能调用以得到一个指向指向组件的DOM node的引用。

findDOMNode() 只作用于已挂载的组件(即,已经放置于DOM中的组件)。如果你尝试在还没有被挂载的组件上调用此函数(比如在render() 里有待被创建的组件上调用 findDOMNode() 将会抛出一个异常)

findDOMNode 已经在 React 0.14 移除,直接使用 this.myTextInput 即可。

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

var MyComponent = React.createClass({
handleClick: function() {
// 明确的强制text input使用原生DOM API。
ReactDOM.findDOMNode(this.refs.myTextInput).focus();
},
render: function() {
// 当组件被挂载时
// ref属性给this.refs添加了一个指向指向组件的引用
return (
<div>
<input type="text" ref="myTextInput" />
<input
type="button"
value="Focus the text input"
onClick={this.handleClick}
/>

</div>
);

}
});

ReactDOM.render(
<MyComponent />,
document.getElementById('example')
);

React Guides

Node.js

模块机制

http://www.infoq.com/cn/articles/nodejs-module-mechanism

#NPM

Package.json

scripts

安装本地模块

有的模块在其它项目中已经 install 过,其它项目也用上的话,可以使用安装本地模块的方式,省去重复安装下载,节省磁盘空间。

第一种方式

是使用 npm link,测试过

首先进入已安装的模块目录,在终端执行 npm link,可能需要使用 sudo

执行完成后进入需要安装该模块的目录,执行 npm link package-name

这种方式不会将模块复制到新安装目录中,而会创建一个链接目录。

第二种方式

是使用 npm install /path,这个方式没实际使用过,好像会将模块复制到新安装目录。

第三种方式

在 NPM 2.0,可以直接把本地路径写入 package.json 的 dependencies 中,也没实际使用:

1
2
3
4

"dependencies": {
"bar": "file:../foo/bar"
}

stackoverflow