diff --git a/tutorial/README.md b/tutorial/README.md new file mode 100644 index 000000000..fcbcf4b08 --- /dev/null +++ b/tutorial/README.md @@ -0,0 +1,13 @@ +# 深入Omi + +(待续...) + +* [环境搭建](./cn_env.md) +* [Hello Omi](./cn_hello.md) +* [手Q附近Web页Omi实战](./cn_nearby.md) +* 局部CSS揭秘 +* 组件嵌套揭秘 +* 事件处理揭秘 +* 服务器端渲染揭秘 +* 模板切换揭秘 +* 容器系统揭秘 \ No newline at end of file diff --git a/tutorial/env.md b/tutorial/env.md new file mode 100644 index 000000000..bd5484d4e --- /dev/null +++ b/tutorial/env.md @@ -0,0 +1,132 @@ +

环境搭建

+ +[Omi框架](https://github.com/AlloyTeam/omi)使用 Webpack + ES6 的方式去开发;使用karma+jasmine来作为Omi的测试工具。 + +## Karma介绍 + +Karma是一个基于Node.js的JavaScript测试执行过程管理工具(Test Runner)。该工具可用于测试所有主流Web浏览器,也可集成到CI(Continuous integration)工具,也可和其他代码编辑器一起使用。这个测试工具的一个强大特性就是,它可以监控(Watch)文件的变化,然后自行执行。但是集成到travis ci要把singleRun设置成true,让其只执行一遍。 + +## Jasmine介绍 +Jasmine 是一款 JavaScript BDD(行为驱动开发)测试框架,它不依赖于其他任何 JavaScript 组件。它有干净清晰的语法,让您可以很简单的写出测试代码。 + +## 开发依赖包 + +在package.json中,有如下配置: + +```js + "devDependencies": { + "babel-core": "^6.0.20", + "babel-loader": "^6.0.1", + "babel-preset-es2015": "^6.0.15", + "node-libs-browser": "^0.5.3", + "webpack": "^1.14.0", + "jasmine-core": "^2.5.2", + "karma": "^1.3.0", + "karma-chrome-launcher": "^2.0.0", + "karma-jasmine": "^1.1.0", + "karma-webpack": "^1.8.1" + } +``` + +* ES6+相关依赖有babel-core、babel-loader和babel-preset-es2015 + +在webpack.config.js中配置js文件使用babel-loader编译。 +```js +loaders: [ + { + loader: 'babel-loader', + test: /\.js$/, + query: { + presets: 'es2015', + } + } +] +``` + +* webpack相关依赖有node-libs-browser和webpack +* 其余都是单元测试相关依赖 + + 注意,这里使用了karma-webpack。因为使用Omi框架支持ES6+和ES5,使用karma-webpack是为了在单元测试里面使用ES6+的import和Class等语法。 + +在karma.conf.js中配置webpack: + +```js + webpack: webpackConfig, + webpackMiddleware:{ + noInfo:false + }, + plugins: [ + 'karma-webpack', + 'karma-jasmine', + 'karma-chrome-launcher' + ] +``` + +具体配置看test目录下的[karma.conf.js](https://github.com/AlloyTeam/omi/blob/master/test/karma.conf.js)和[webpack.test.config.js](https://github.com/AlloyTeam/omi/blob/master/test/webpack.test.config.js)便可。 + +注意,karma.conf.js需要设置 + +```js +// if true, Karma captures browsers, runs the tests and exits +singleRun: true, +``` + +不然,travis ci脚本执行的时候不会中断导致执行超时异常。 + +## npm 脚本 + +```js + "scripts": { + "build": "webpack -w", + "test": "karma start test/karma.conf.js", + "hello": "webpack -w", + "todo": "webpack -w" + } +``` + +其中: +* npm run build : 生成dist目录的omi.js文件 +* npm run test : 执行单元测试 +* npm run hello : 编译hello的demo +* npm run todo : 编译todo的demo + +在webpack.config.js中,会根据 process.env.npm_lifecycle_event去设置不同的入口文件。所以同样是执行webpack -w,执行结果可以不一样。 + +来看下build的相关webpack配置: + +```js +if(ENV === 'build'){ + config = { + entry: { + omi: './src/index.js' + }, + output: { + path: 'dist/', + library:'Omi', + libraryTarget: 'umd', + filename: '[name].js' + }, +``` + +这里把libraryTarget设置成了umd,webpack会帮助我们build出umd的Omi。 + +如果是打包demo(npm run hello 和 npm run todo)的话,会进入下面的条件判断: + +```js +else { + config.entry = './example/' + ENV + '/main.js'; + config.output.path = './example/' + ENV + '/'; +} +``` + +会去example下对应的目录查找main.js作为webpack入口文件。 + +这里可以看到,我们不仅用webpack build出Omi框架,也使用webpack build所有demo。 +详细配置参考[webpack.config.js](https://github.com/AlloyTeam/omi/blob/master/webpack.config.js)的配置。 + +## 参考文档 + +* [http://www.cnblogs.com/cqhaibin/p/5867125.html](http://www.cnblogs.com/cqhaibin/p/5867125.html) +* [https://karma-runner.github.io/latest/intro/installation.html](https://karma-runner.github.io/latest/intro/installation.html) +* [https://karma-runner.github.io/latest/intro/configuration.html](https://karma-runner.github.io/latest/intro/configuration.html) + diff --git a/tutorial/nearby.md b/tutorial/nearby.md new file mode 100644 index 000000000..10e7380d2 --- /dev/null +++ b/tutorial/nearby.md @@ -0,0 +1,262 @@ +## 写在前面 +Omi很适合大型复杂的Web页面开发,例如一些Web在线工具的开发。但是制作这种简单的QQ附近用户列表Web页,也不会有大炮哄蚊子的感觉。 + +项目开始之前,实现选择一个脚手架。可以使用[omi-cli](https://github.com/AlloyTeam/omi/tree/master/cli)快速创建项目脚手架。脚手架主要基于 Gulp + Webpack + Babel + BrowserSync 进行开发和部署。 + +Gulp用来串联整个流程,Webpack + Babel让你可以写ES6和打包,BrowserSync用来帮你刷浏览器,不用F5了。 + +这里需要注意的是,BrowserSync会启动localhost:3000导致你的AJAX请求跨域而无法拿到数据。 + +![](http://images2015.cnblogs.com/blog/105416/201702/105416-20170222184323476-1150194475.png) + + +所以,要使用Fiddler并配置Extention: + +![](http://images2015.cnblogs.com/blog/105416/201702/105416-20170222184331445-1124037886.png) + + +## 目录 + +![](http://images2015.cnblogs.com/blog/105416/201702/105416-20170222184337726-1825094998.png) + + +目录结构也是和[Omi Github上的scaffolding](https://github.com/AlloyTeam/omi/tree/master/scaffolding)一样。 +组件全放在component目录,公共的工具库放在common,其他资源文件放在asset里。 + +## 命令 + +开发 +```js +npm run dev +``` + +发布 +```js +npm run dist +``` + +## 开始写码 + +万事具备,开始写码。先写组件: + +```js +import Omi from 'omi' + +class UserList extends Omi.Component { + constructor(data) { + super(data) + } + + install() { + this.data.uin_info || (this.data.uin_info = []) + this.data.uin_info.forEach(user => { + this.prepareData(user) + }) + } + + prepareData(user){ + user.desc_d = user.desc.split(" ")[0] + user.desc_t = user.desc.split(" ")[1] + user.isBoy = user.sex === "男" + user.qlogo = user.url.replace("http://", location.protocol + "//").replace(/&/g, "&") + if (user.profession_desc) { + user.hasProfession_desc = true + } + } + + appendUsers (users) { + users.uin_info && users.uin_info.forEach(user =>{ + this.prepareData(user) + this.data.uin_info.push(user) + }) + this.update() + } + + sendGift(uin, nick, qlogo) { + //送礼物并关闭webview,此处省略 + //.. + //.. + } + + render() { + return ` +
+ {{#uin_info}} +
+ +
+
{{{nick}}}
+
+ {{#isBoy}}{{age}} {{/isBoy}} + {{^isBoy}}{{age}} {{/isBoy}} + {{#hasProfession_desc}} {{profession_desc}} {{/hasProfession_desc}} +
+
{{{intro}}}
+
+
{{desc_d}} · {{desc_t}}
+
+ {{/uin_info}} +
加载中...
+
+` + } + + style() { + return ` + + +.qlogo { + overflow: hidden; + width: 70px; + height: 70px; + -webkit-border-radius: 50%; + border-radius: 50%; + position: absolute; + top: 10px; + left: 12px; +} +... +... +..这里省略大量..... +... +... + +.distance_info { + position: absolute; + top: 15px; + right: 9px; + color: #7B7B84; + font-size: 10px; +} + + ` + } +} + +export default UserList +``` + +组件里面有5个方法: + +* constructor 组件的构造函数,生命周期的一部分,其实在super上面和super调用下面可以对data做一些处理。super之上不能拿到this +* install 组件的初始化安装,生命周期的一部分,这里也可以拿到用户传进的data进行处理 +* prepareData 对数据进行一些处理来满足模板的渲染 +* appendUsers 新增数据,用来处理用户向下滚动的load more 的行为的时候调用 +* sendGift 送礼物,点击每一项的时候会有送礼物的行为,业务相关,可以无视.. + +其他两个方法的render和style用来生成组件的HTML和局部CSS,不再叙述。 +render里面使用了[mustache.js](https://github.com/janl/mustache.js)模板引擎; +如果使用omi.lite.js版本(不包含[mustache.js](https://github.com/janl/mustache.js)模板引擎)的话,你也可以使用ES6 map去遍历数据生成HTML,或者重写 Omi.template去使用任意你喜欢的模板引擎,非常灵活方便。 + +这里友情提醒一下,如果使用webstorm的话,可以把js version设置成JSX Harmony或者ECMAScript 6,这样才是写ES6+的姿势。 + +![](http://images2015.cnblogs.com/blog/105416/201702/105416-20170222184433148-472636444.png) + + +下面来看index.js: + +```js +import Root from './config.js' +import Omi from 'omi' +import UserList from '../component/user_list/index.js' + +Omi.makeHTML('UserList', UserList) + +class Main extends Omi.Component { + constructor(data) { + super(data) + } + + installed() { + window.onscroll = () => this.loadMore() + this.requestData(data => this.list.appendUsers(data)) + } + + loadMore() { + const body = document.body, + html = document.documentElement, + height = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight), + vp_height = window.innerHeight + + if (height - document.body.scrollTop - vp_height < 200) { + this.requestData(data => this.list.appendUsers(data)) + } + } + + requestData(callback) { + if (Root.isDev) { + require.ensure([], ()=> { + callback(require('./mock_data.js').default) + }) + }else{ + //ajax 请求数据,这里省略 + } + } + + render() { + return ` +
+ +
` + } +} + +Omi.render(new Main(),'body') +``` + +通过Omi.makeHTML('UserList', UserList)这句代码,UserList变成了可以嵌套至render方法中的标签。如: + +```js + render() { + return ` +
+ +
` + } +``` + +下面这行代码,是监听滚动,快滚动到底部的时候在loadMore里面会去请求。 + +```js +window.onscroll = () => this.loadMore() +``` + +通过height - document.body.scrollTop - vp_height < 200判断用户快要滚动底部,滚动到底部有个加载更多的行为,即: + +```js +if (height - document.body.scrollTop - vp_height < 200) { + this.requestData(data => this.list.appendUsers(data)) +} +``` + +requestData是去服务器请求分页的数据,请求成功,会去调用this.list.appendUsers进行数据的添加。 +慢着?this.list哪里来的?appendUsers又是哪里定义的方法?且看下面: + +```js + +``` + +上面标记的name,让你可以直接通过this.list访问到UserList对象的实例,所以也就可以调用它的appendUsers方法! + +再来看下数据模拟: + +```js +if (Root.isDev) { + require.ensure([], ()=> { + callback(require('./mock_data.js').default) + }) + } +``` + +这里在dev环境下是mock数据,使用了require.ensure,这样当你npm run dist的时候,mock的数据就不会被打包进js里了!! + +## 最后 + +好了,就这么多,Omi让代码真心方便简洁~~~ + +### 相关地址 + +* [演示地址](http://alloyteam.github.io/omi/example/qq_nearby/dev/index.html) +* [源码地址](https://github.com/AlloyTeam/omi/tree/master/example/qq_nearby) diff --git a/tutorial/omi-cli.md b/tutorial/omi-cli.md new file mode 100644 index 000000000..ff30fb288 --- /dev/null +++ b/tutorial/omi-cli.md @@ -0,0 +1,140 @@ +# Omi命令行界面omi-cli发布 + +通常认为,命令行界面(CLI)没有图形用户界面(GUI)那么方便用户操作。但是CLI比GUI节约资源,在熟悉命令之后,CLI会比GUI更加高效地帮你完成想要的任务。 + +下面来介绍下[pasturn](https://github.com/pasturn)童鞋为Omi开发的CLI的两种使用姿势: + +## 姿势一 + +```js +$ npm install omi-cli -g //安装cli +$ omi init your_project_name //初始化项目 +$ cd your_project_name //转到项目目录 +$ npm run dev //开发 +$ npm run dist //部署发布 +``` + +## 姿势二 + +当我们在一个空文件夹的时候,可以执行下面的命令。 + +```js +$ npm install omi-cli -g //安装cli +$ omi init //初始化项目 +$ npm run dev //开发 +$ npm run dist //部署发布 +``` + +这里不用再去跳转目录了,当前目录就是项目的目录。 + +## 安装过程截图 + +安装omi-cli: + +![](http://images2015.cnblogs.com/blog/105416/201702/105416-20170227100545470-696026058.png) + + +安装初始化项目omi init: + +![](http://images2015.cnblogs.com/blog/105416/201702/105416-20170227100554891-1802174132.png) + +上面的成功的界面。注意:初始化项目会自动安装相关的npm依赖包,所以时间较长,请耐心等待。 +安装完成之后,在项目目录下你可以看到下面的目录结构: + +![](http://images2015.cnblogs.com/blog/105416/201702/105416-20170227100755845-465268116.png) + +开发 npm run dev: + +![](http://images2015.cnblogs.com/blog/105416/201702/105416-20170227100601235-1477801934.png) + +![](http://images2015.cnblogs.com/blog/105416/201702/105416-20170227100608985-921528126.png) + +如果,你看到了上面的界面,说明一切OK了。创建出来的项目主要基于 Gulp + Webpack + Babel + BrowserSync 进行开发和部署。Gulp用来串联整个流程,Webpack + Babel让你可以写ES6和打包,BrowserSync用来帮你刷浏览器,不用F5了。 + +## 组件开发 + +页面的组件都在component目录: + +![](http://images2015.cnblogs.com/blog/105416/201703/105416-20170309091322484-527946546.png) + +你可以把组件的HTML、CSS和JS分离成三个文件,然后通过require的方式使用,如: + +```js +import Omi from 'omi'; + +const tpl = require('./index.html'); +const css = require('./index.css'); + +class Footer extends Omi.Component { + constructor (data) { + super(data); + } + + style () { + return css; + } + + render () { + return tpl; + } +} + +export default Footer; +``` + +也可以直接all in js的方式: + +```js +import Omi from 'omi'; + +class Header extends Omi.Component { + constructor (data) { + super(data); + } + + style () { + return ` + + `; + } + + render () { + return ` +
+
+ +
+ +
`; + } +} + +export default Header; +``` + +如果需要更多动态编程能力,可以all in js。如果纯静态不怎么需要改动的话,直接分离成三个文件通过require进来便可。 + +## 后续 + +更多脚手架模板以及更多功能的命令正在开发中,如果有什么意见或者建议欢迎让我们知道。 + +## 相关 + +* Omi的Github地址[https://github.com/AlloyTeam/omi](https://github.com/AlloyTeam/omi) +* 如果想体验一下Omi框架,可以访问 [Omi Playground](http://alloyteam.github.io/omi/example/playground/) +* 如果想使用Omi框架或者开发完善Omi框架,可以访问 [Omi使用文档](https://github.com/AlloyTeam/omi/tree/master/docs#omi使用文档) +* 如果你想获得更佳的阅读体验,可以访问 [Docs Website](http://alloyteam.github.io/omi/website/docs.html) +* 如果你懒得搭建项目脚手架,可以试试 [omi-cli](https://github.com/AlloyTeam/omi/tree/master/cli) +* 如果你有Omi相关的问题可以 [New issue](https://github.com/AlloyTeam/omi/issues/new) +* 如果想更加方便的交流关于Omi的一切可以加入QQ的Omi交流群(256426170) + +![](http://images2015.cnblogs.com/blog/105416/201702/105416-20170208095745213-1049686133.png) \ No newline at end of file diff --git a/tutorial/webpack2-sass-support.md b/tutorial/webpack2-sass-support.md new file mode 100644 index 000000000..d632326d6 --- /dev/null +++ b/tutorial/webpack2-sass-support.md @@ -0,0 +1,155 @@ +# omi-cli新版发布-升级webpack2和支持sass生成组件局部CSS + +[omi-cli](https://github.com/AlloyTeam/omi/tree/master/cli)是[Omi](https://github.com/AlloyTeam/omi)的命令行工具。在v0.1.X以及之前版本中,生成出来的项目脚手架 +是基于webpack1的。由于: + +* webpack1不支持tree-shaking,webpack2 支持tree-shaking +* webpack1不支持 sass-loader,webpack2 支持sass-loader + +tree-shaking 作用是移除没有使用的代码有效的减小包体积 +sass-loader 可以让你把sass转成css,在omi项目里可以把sass转成组件的局部CSS + +所以,果断把omi-cli的项目模板升级为基于webpack2。感兴趣的同学可以立马尝试下。 + +```js +$ npm install omi-cli -g //安装cli +$ omi init your_project_name //初始化项目 +$ cd your_project_name //转到项目目录 +$ npm run dev //开发 +$ npm run dist //部署发布 +``` + +## 采坑之路 + +在升级的过程中遇到了不少问题,这里记录一下。 + +### 问题1 + +![](http://images2015.cnblogs.com/blog/105416/201703/105416-20170316082316448-1429354256.jpg) + + +webpack2中,CommonsChunkPlugin不再支持上面的传参形式,必须传JSON形式。 + +### 问题2 + +![](http://images2015.cnblogs.com/blog/105416/201703/105416-20170316082330885-1671968338.jpg) + + +webpack2中,不在允许省略-loader的形式标记loader,如: + +```js + {test: /\.html$/, loader: "string"}, +``` + +都要改成: + +```js + {test: /\.html$/, loader: "string-loader"}, +``` + +### 问题3 + +![](http://images2015.cnblogs.com/blog/105416/201703/105416-20170316082340745-1920766716.jpg) + + +使用webpack-stream的同学可能会碰到上面这个错误。找了好久发现: + +![](http://images2015.cnblogs.com/blog/105416/201703/105416-20170316082349604-1404731044.jpg) + + +在gulp里要修改下第二个参数,把 null 改成 require('webpack')。 + +## sass生成组件局部CSS + +在传统的webpack项目脚手架中,都会包含css相关的三个loader: + +```js +// webpack.config.js +module.exports = { + ... + module: { + rules: [{ + test: /\.scss$/, + use: [{ + loader: "style-loader" // creates style nodes from JS strings + }, { + loader: "css-loader" // translates CSS into CommonJS + }, { + loader: "sass-loader" // compiles Sass to CSS + }] + }] + } +}; +``` + +* sass-loader负责把sass编译成css +* css-loader负责把编出来的css转成CommonJS模块用于js里面进行require获取 +* style-loader负责把css插入到页面的head里面 + +那么问题来了?Omi内部本身组件可以定义style方法,如: + +```js +class Hello extends Omi.Component { + ... + style () { + return ` + h1{ + cursor:pointer; + } + `; + } + ... +} +``` + +在Omi内部的管线里面,会把执行style方法,把返回的css转成局部css,然后插入到页面的head里面。所以和webpack三个loader里的管线有冲突!怎么解决?去掉一个loader便可! + +```js +// webpack.config.js +module.exports = { + ... + module: { + rules: [{ + test: /\.scss$/, + use: [{ + loader: "css-loader" // translates CSS into CommonJS + }, { + loader: "sass-loader" // compiles Sass to CSS + }] + }] + } +}; +``` + +我们只需要能够在js里动态获取到编译好的css字符串,然后拼在style方法里便可!两个管线就打通了!具体代码: + +```js +import Omi from 'omi'; + +const style = require('./index.scss'); + +class Header extends Omi.Component { + ... + style () { + return style.toString(); + `; + } + ... +} + +export default Header; +``` + +这里需要注意require到的style不是字符串对象,需要执行toString才能获取到css字符串。 + +## 相关 + +* Omi的Github地址[https://github.com/AlloyTeam/omi](https://github.com/AlloyTeam/omi) +* 如果想体验一下Omi框架,可以访问 [Omi Playground](http://alloyteam.github.io/omi/example/playground/) +* 如果想使用Omi框架或者开发完善Omi框架,可以访问 [Omi使用文档](https://github.com/AlloyTeam/omi/tree/master/docs#omi使用文档) +* 如果你想获得更佳的阅读体验,可以访问 [Docs Website](http://alloyteam.github.io/omi/website/docs.html) +* 如果你懒得搭建项目脚手架,可以试试 [omi-cli](https://github.com/AlloyTeam/omi/tree/master/cli) +* 如果你有Omi相关的问题可以 [New issue](https://github.com/AlloyTeam/omi/issues/new) +* 如果想更加方便的交流关于Omi的一切可以加入QQ的Omi交流群(256426170) + +![](http://images2015.cnblogs.com/blog/105416/201702/105416-20170208095745213-1049686133.png) \ No newline at end of file