沉寂了兩個月,我又回來了。javascript
跟大家猜的同樣,我已經到淘系實習了一段時間了,從上一篇文章以後就放了更多的心思在工做上。上篇文章發出去以後,我去騰訊實習了一段時間,等待阿里實習生入職流程開啓。css
收到淘系的實習生 offer 後,我買了人生中的第一張機票,第一次坐上了飛機,來到了一個陌生的城市——杭州。乾淨的街道、寬敞的瀝青馬路,吸引了我這個來自小城市的年輕人。html
博客好久沒更新了,不論是我的的網站仍是掘金,都不多有更新了,偶爾上掘金看看一些好文,總想更新一下,但又沒找到好的題材。如今實習了一段時間後,上手了淘系的開源跨端框架——Rax,新奇又好玩,也總結了一些很基礎的開發技巧,補充一下官方文檔的缺漏(官方文檔對於新手來說確實不太友好)。前端
基礎部分演示項目 Git
倉庫:Rax-TODOjava
進階技巧演示項目 Git
倉庫:Software-Engineering(同時也是個人課程設計,歡迎大夥點個 Star
)node
前面的部分是針對只有一點 React
基礎的同窗的,高端玩家請點擊:進階技巧react
這部分官網文檔比較詳細了,先看一下官網文檔:快速開始webpack
官網文檔提供的方案還挺多的,可能會選擇困難,咱們就從最基礎的 TODO 開始,使用 Rax 搭建一個 Web 項目:git
npm init rax todo-list
複製代碼
輸入上面的命令以後,會使用 npx
安裝 rax-cli
腳手架工具,安裝完成後會彈出這樣的界面:github
added 106 packages from 53 contributors in 10.5s
? What's your project type? (Use arrow keys)
❯ App (Build universal application)
Component (Build universal component)
API (Build universal API library)
Plugin (Build plugin for miniapp)
複製代碼
使用 上下鍵
移動箭頭,後面的操做同理。
這裏咱們就直接使用默認的選項,建立一個 APP,按下回車。
? Choose targets your project want to run? (Press <space> to select, <a> to togg
le all, <i> to invert selection)
❯◉ Web
◯ Weex
◯ Kraken (Flutter)
◯ Alibaba MiniApp
◯ WeChat MiniProgram
複製代碼
使用 空格鍵
選擇,按下 字母a
能夠全選,這裏咱們就先選擇 Web,後續若是有編譯爲 Weex
和 小程序
的需求能夠在項目目錄的 build.json
中添加。
編譯爲 Flutter
應用的功能目前還不穩定,且坑比較多,建議動手能力強的玩家嘗試,小白就先繞道吧。
? What's your application type? (Only valid in target: Web/Weex/Kraken) (Use arr
ow keys)
❯ Single-page application (SPA)
Multi-page application (MPA)
Create lite application (The simplest project setup)
複製代碼
選擇 APP 的類型,是 SPA
仍是 MPA
,這裏的選項只在 Web/Weex/Kraken(Flutter)
應用中有效。
咱們選擇默認的選項 SPA
,不明白 SPA
和 MPA
的區別的同窗,能夠移步文章:認識單頁應用(SPA)與多頁應用(MPA)
? What's author's name? (rax): 熾翎
複製代碼
? What type of language do you want to use? (Use arrow keys)
❯ JavaScript
TypeScript
複製代碼
這裏咱們就先選擇 JavaScript
,後續若是有引入 TypeScript
的需求,能夠手動引入。
? Do you want to enable these features? (Press <space> to select, <a> to toggle
all, <i> to invert selection)
❯◯ Server-side rendering (SSR) (Only valid in target: Web)
◯ Aliyun Function Compute (FaaS) (Only valid in target: Web)
◯ Compatibility with React
複製代碼
這裏咱們就什麼都不選。
固然,若是有須要咱們能夠選擇 Compatibility with React
配置與 React 的兼容,也能夠爲咱們的應用開啓 服務端渲染(SSR)
。若是有需求開啓 Serverless
,也能夠選擇開啓 功能即服務(FaaS)
。SSR
和 FaaS
都只能在 Web
應用中有效。
不懂 SSR
和 Serverless
的同窗請自行百度(Google)哈。
? Do you want to install dependences automatically after initialization? (Y/n)
複製代碼
這裏就直接回車,這個選項的意思是詢問是否在初始化完成後自動安裝依賴。默認是 Yes
,就不用咱們手動進入項目文件夾執行 npm install
了。
To run your app:
cd todo-list
npm start
複製代碼
當終端出現這一段文字的時候,就說明依賴安裝完了,咱們能夠進入到項目的文件夾下,執行 npm start
跑起咱們的項目。
在執行 npm start
後,終端會顯示:
Rax development server has been started:
[Web] Development server at:
http://localhost:3333/
複製代碼
這時候咱們就能夠在瀏覽器輸入 Dev Server
的地址,查看咱們的項目了。
隨便選擇一款本身喜歡的 IDE,進入項目的文件夾,咱們會看到這樣的一個目錄結構:
.
├── README.md # 項目說明
├── build.json # 項目構建配置
├── package.json
└── src # 源碼目錄
├── app.js # 應用入口文件
├── app.json # 應用配置,包括路由配置,小程序 window 配置等
├── public # (可選)靜態資源目錄,會拷貝內容至 build 目錄
├── components # 應用的公共組件
│ └── Logo # 組件
│ ├── index.css # Logo 組件的樣式文件
│ └── index.jsx # Logo 組件 JSX 源碼
├── document # 頁面的 HTML 模板
│ └── index.jsx
└── pages # 頁面
└── Home # home 頁面
└── index.jsx
複製代碼
開發以前,咱們須要瞭解一些 Rax 下的一些小規矩:
rpx
:默認以 750rpx
爲屏幕寬度,即 1rpx = 1/750 * 屏幕寬度
。樣式簡寫
:在 Rax 中,因爲目前兼容性的問題,不支持 部分
樣式簡寫,例如:在寫 border
樣式時,應該將各個部分分開:border-width
、border-style
、border-color
。在遇到屬性簡寫在非 Web 平臺不生效的問題時,嘗試將屬性分開或許就能解決問題。咱們要作的第一件事是刪除初試化後默認的 Home
頁面中的 Logo
組件,在 src/components
文件夾下,整個刪除。
刪除以後,src/pages/Home/index.jsx
確定會報錯,咱們先稍做修改,改爲 Hello World!
。
// index.jsx
import { createElement } from 'rax';
import View from 'rax-view';
import Text from 'rax-text';
import './index.css';
export default function Home() {
return (
<View className="home"> <Text>Hello World!</Text> </View>
);
}
複製代碼
這個時候會發現頁面變成了 Hello World!
,後面應該不用講解太多,就是使用 React
應用開發的方式,開始愉快的 coding
過程。
可能你會發現,爲何在這裏咱們只能用 Rax 提供的 View
和 Text
組件?
由於爲了使跨端顯示效果一致,Rax 爲開發者抹平了不一樣平臺之間樣式顯示不一致的問題。
固然,若是你只是想用 Rax 作一個 Web 應用,那你可使用 HTML
標籤(汗)。
View
組件和 Text
組件的用法能夠移步文檔:基礎組件-View 和 基礎組件-Text
按照 耦合度
,咱們應該將組件放在 src/pages/Home
文件夾下。
在這個文件夾下,咱們建立一個這樣的目錄:
src/pages/Home
└── components # 組件文件夾
└── ListItem # ListItem 組件
├── index.css # CSS
└── index.jsx # JSX
複製代碼
首先咱們應該構思一下這個 ListItem
應該暴露給父組件哪些接口:
id
onClick
事件明確了組件應該暴露的接口以後,就能夠開始寫代碼了:
// index.jsx
import { createElement } from 'rax';
import View from 'rax-view';
import Text from 'rax-text';
import './index.css';
const ListItem = (props) => {
// 解構 id 完成狀態 內容 onClick 事件
const { id, done, content, onClick } = props;
// 完成項文字樣式
const style = {
fontSize: '64rpx',
lineHeight: '96rpx',
textDecoration: done && 'line-through'
};
return (
<View className="list-item" onClick={() => onClick(id)}> <View className="list-dot"></View> <Text style={style}>{content}</Text> </View>
);
};
export default ListItem;
複製代碼
/* index.css */
.list-item {
flex-direction: row;
justify-content: flex-start;
align-items: center;
width: 100%;
height: 100rpx;
}
.list-dot {
width: 20rpx;
height: 20rpx;
margin-right: 20rpx;
border-radius: 10rpx;
background-color: #333;
}
複製代碼
仍是同樣,先建立文件:
src/pages/Home
└── components # 組件文件夾
├── List # List 組件
│ ├── index.css # CSS
│ └── index.jsx # JSX
└── ListItem # ListItem 組件
├── index.css # CSS
└── index.jsx # JSX
複製代碼
建立一個 List
組件做爲 ListItem
的容器管理全部的 Item
,實現 添加/刪除
的功能。
既然要作 添加
功能,那輸入框確定是必不可少的,在咱們建立的項目裏,默認是沒有輸入框組件的依賴的,因此咱們要先安裝 Rax 提供的輸入框組件:
npm install rax-textinput --save
複製代碼
組件的用法能夠轉戰文檔:基礎組件-TextInput
// index.jsx
import { createElement, useState } from 'rax';
import View from 'rax-view';
import Text from 'rax-text';
import TextInput from 'rax-textinput';
import ListItem from '../ListItem';
import './index.css';
const List = () => {
// 初始化 itemId 每次添加新列表項就 +1
const [itemId, setItemId] = useState(0);
// 初始化列表
const [list, setList] = useState([]);
// 初始化 TextInput 內容
const [inputValue, setInputValue] = useState('');
// 輸入框輸入事件
const handleUserInput = (e) => {
setInputValue(e.target.value);
};
// 添加按鈕點擊事件
const handleAddButtonClick = () => {
// 構造列表項數據結構
const item = {
id: itemId,
content: inputValue,
done: false
};
// immutable 思想 生成新的引用
const newList = [...list, item];
setList(newList);
// 清空輸入框
setInputValue('');
// itemId ++
setItemId(itemId + 1);
};
// 列表項點擊事件
const handleItemClick = (id) => {
// 遍歷列表 當事件未完成時標記爲已完成 當事件已完成時刪除
const newList = list.filter((item) => {
if (item.id === id) {
if (item.done) {
return false;
} else {
item.done = true;
}
}
return true;
});
setList(newList);
};
return (
<View className="list"> <View className="list-input-wrapper"> <TextInput className="list-input" value={inputValue} onInput={handleUserInput} /> <View className="list-add-button" onClick={handleAddButtonClick}> <Text>添加</Text> </View> </View> <View className="list-item-wrapper"> {list.map((item) => ( <ListItem key={item.id} id={item.id} content={item.content} done={item.done} onClick={handleItemClick} /> ))} </View> </View>
);
};
export default List;
複製代碼
.list {
align-items: center;
}
.list-input-wrapper {
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
.list-add-button {
justify-content: center;
align-items: center;
width: 150rpx;
height: 100rpx;
margin-left: 20rpx;
border-radius: 10px;
background-color: #dcdcdc;
}
.list-input {
width: 100%;
height: 100rpx;
line-height: 96rpx;
font-size: 64rpx;
border-width: 1px;
border-color: #dcdcdc;
}
.list-item-wrapper {
width: 100%;
}
複製代碼
至此,組件的基本功能就完成了。
如今咱們要將組件引入首頁 Home
中:
import { createElement } from 'rax';
import View from 'rax-view';
import List from './components/List';
import './index.css';
export default function Home() {
return (
<View className="home"> <List /> </View>
);
}
複製代碼
如今就能看到一個能完成基本功能的 TODO 應用了。
我猜,有些高玩看到我前面寫的部分,確定會說:
啊~ 你這個這麼簡單,不是有手就行嘛~
有一說一,確實有手就行(誤)。
前面的部分只是寫給不愛讀官方文檔或者只有一點 React
基礎的同窗的,從這個部分開始,會開始引入一些高級的內容。
在前面建立的項目中,咱們會看到項目的文件夾下有一個 app.json
文件,打開它你會看到:
{
"routes": [
{
"path": "/",
"source": "pages/Home/index"
}
],
"window": {
"title": "Rax App"
}
}
複製代碼
這個文件就是用來控制項目路由,若是後續的開發增長了更多的頁面,就須要在 routes
下添加對象。path
就是你新加入的頁面的訪問路徑(URL
),source
就是你新添加的頁面所在的文件目錄。
在項目文件夾下還有一個 build.json
文件,裏面的內容是這樣的:
{
"plugins": [
[
"build-plugin-rax-app",
{
"targets": ["web"]
}
]
]
}
複製代碼
由於咱們以前建立項目的時候,選擇的是單頁應用(SPA),咱們想改爲多頁應用怎麼辦? 很簡單:
先安裝多頁應用依賴:
npm install build-plugin-rax-multi-pages --save-dev
複製代碼
而後在 targets
所在的對象下,增長一個鍵 "type"
,值爲 "mpa"
,像這樣:
{
"plugins": [
[
"build-plugin-rax-app",
{
"targets": ["web"],
"type": "mpa"
}
]
]
}
複製代碼
重啓 Dev Server
後會發現頁面變成了這樣:
咱們在最開始建立項目的時候,並無選擇多平臺,可是 Rax 做爲一個跨端開發框架,不添加跨端支持怎麼行,這裏會給一個教程教你們添加跨端支持(固然一開始就選擇好更方便)。
仍是 build.json
文件,有一個 targets
數組,想要添加跨端支持,就在數組中添加對應的內容便可:
{
"plugins": [
[
"build-plugin-rax-app",
{
"targets": ["web", "weex", "miniapp", "wechat-miniprogram"],
"type": "mpa"
}
]
]
}
複製代碼
weex
對應 Weex
平臺,miniapp
和 wechat-miniprogram
對應 阿里小程序
和 微信小程序
。
咱們在 build.json
中使用了上面的配置後,頁面會變成這個樣子:
點開 Weex Preview
後,你會發現是一堆 JavaScript
代碼,那怎麼預覽?
有一個稍微麻煩一點的方法,就是先肯定你本機在內網的 IP
地址,將 URL
中的 localhost
改爲內網 IP
,而後將 URL
轉換爲二維碼(Chrome
有不少插件能夠實現)。
在手機上下載 Weex Playground
而後掃描二維碼,就能在手機上預覽到 APP 的效果。
在前面的項目裏,咱們沒有引入 TypeScript
,那若是想引入應該怎麼辦?
很簡單,在官網的文檔中也有講解:項目開發- TypeScript 支持
只須要先安裝 rax-types
和 typescript
:
npm install rax-types typescript --save-dev
複製代碼
在項目文件夾下建立 tsconfig.json
文件,使用如下配置:
{
"compilerOptions": {
"module": "esNext",
"target": "es2015",
"outDir": "build",
"jsx": "preserve",
"jsxFactory": "createElement",
"moduleResolution": "node",
"sourceMap": true,
"alwaysStrict": true,
"baseUrl": ".",
"paths": {
"rax": ["node_modules/rax-types"]
}
},
"include": ["./src/**/*"]
}
複製代碼
便可添加 TypeScript
支持。
這是一個比較大的坑,以致於讓我摸索了一個上午才解決。
Rax 官方文檔中並無自動化測試相關配置的教程,以致於我只能直接在釘釘找負責 Rax 維護的師兄求助,歷經千辛萬苦終於將測試用例跑通。
首先,咱們須要安裝幾個依賴,數量有點多,建議分開安裝:
npm install jest --save-dev
npm install @types/jest --save-dev
npm install @babel/preset-env --save-dev
npm install babel-jest --save-dev
npm install rax-test-renderer --save-dev
npm install @babel/plugin-proposal-class-properties --save-dev
npm install @babel/plugin-proposal-decorators --save-dev
npm install @babel/plugin-proposal-export-default-from --save-dev
npm install @babel/preset-flow --save-dev
npm install @babel/preset-react --save-dev
npm install babel-plugin-transform-jsx-stylesheet --save-dev
複製代碼
依賴安裝好以後,在項目的目錄下建立 .babelrc.js
對 Babel
進行配置:
module.exports = function (api) {
// Cache the returned value forever and don't call this function again.
if (api) api.cache(true);
return {
presets: [
'@babel/preset-flow',
[
'@babel/preset-env',
{
loose: true
}
],
[
'@babel/preset-react',
{
pragma: 'createElement'
}
]
],
plugins: [
'@babel/plugin-proposal-export-default-from',
['@babel/plugin-proposal-class-properties', { loose: false }],
'babel-plugin-transform-jsx-stylesheet',
['@babel/plugin-proposal-decorators', { legacy: true }]
],
ignore: ['build', 'coverage', 'node_modules']
};
};
複製代碼
配置好以後,Jest
仍是沒法跑通的,要在 package.json
中添加啓動腳本:
{
"scripts": {
"build": "build-scripts build",
"start": "build-scripts start",
"lint": "eslint --ext .js --ext .jsx ./",
// 添加 jest 啓動腳本
"test": "jest"
}
}
複製代碼
同時,還要對 Jest
進行配置:
{
"jest": {
"collectCoverage": true,
"moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
"\\.(css|less)$": "<rootDir>/__mocks__/styleMock.js"
}
}
}
複製代碼
配置添加完成以後,在項目的目錄下建立一個文件夾:
.
└── __mocks__
├── fileMock.js # 靜態資源處理
└── styleMock.js # 樣式處理
複製代碼
分別在兩個文件中加入添加代碼:
// fileMock.js
module.exports = 'test-file-stub';
複製代碼
// styleMock.js
module.exports = {};
複製代碼
配置這個部分的目的是讓 Jest
處理樣式和靜態資源時到 mock 的文件夾下找。由於在測試的時候,測試腳本並不會真的去訪問引入的靜態資源,遇到須要引用靜態資源的地方就引導到 __mocks__
文件夾下查找,就不會由於找不到資源而報錯。配置來源於 Jest
官網處理靜態文件。
一切配置完成後,就能夠在一個須要作測試的組件下建立 __test__
文件夾並將測試腳本放在文件夾下了,或者你也能夠直接使用 xxx.test.jsx
爲文件命名,這些文件都會被 Jest
識別爲測試腳本。
除了 Jest
,Rax 團隊還爲 Rax 提供了 Enzyme
適配器,示例項目:raxjs/enzyme-adapter-rax,提供了 Enzyme
的使用示例。
得益於淘系強大的技術實力,Rax 的相關生態相對比較完善。同時,性能也很是強大,這裏我就不過多介紹了。
Rax 在小程序上的性能甚至優於市面上其餘小程序框架:Rax ——完美融合編譯時與運行時的雙引擎小程序框架。
其餘的一些介紹 Rax 的文章能夠移步官網的博客。
文章的最後,推薦一下本身的公衆號:Hello FE,是我和跟我一塊兒實習的小夥伴們一塊兒運營的,會按期分享一些乾貨。
同時,關注公衆號還有前端書籍大全一份贈送,歡迎你們關注,也歡迎你們進羣交流: