了減少產品和前端開發人員之間的矛盾,不斷降本提效,美團醫藥技術部構建了跨端一體化富文本管理平臺Page-佩奇。本文系統介紹了該平臺的定位、設計思路、實現原理以及取得的成效。希望這些實戰經驗與總結,能給大家帶來一些啟發或幫助。
在互聯網圈,開發和產品經理之間相愛相殺的故事,相信大家都有所耳聞。歸根結底,往往都是從簡單的改需求開始,然后你來我往、互不相讓,接著吵架斗嘴,最后導致矛盾不斷升級,甚至帶來比較嚴重的后果。
圖1
在這種背景下,如果把一些功能相對簡單的、需求變動比較頻繁的頁面,直接交給產品或者運營自己去通過平臺實現,是不是就可以從一定程度上減少產品和開發人員之間的矛盾呢?
當然上述的情況,美團也不例外。近些年,美團到家事業群(包括美團外賣、美團配送、閃購、醫藥、團好貨等)的各個業務穩步發展,業務前端對接的運營團隊有近幾十個,每個運營團隊又有不同的運營規則,這些規則還存在一些細微的樣式差別,同時規則內容還會隨著運營季節、節日、地理位置等進行變化和更新。這些需求具體來說有以下幾個特點:
基于以上特點,為了提高研發效率,美團醫藥技術部開始構建了一個跨端一體化富文本管理平臺,希望提供解決這一大類問題的產研方案。不過,部門最初的目標是開發一套提效工具,解決大量諸如幫助文檔、協議頁、消息通知、規則說明等靜態頁面的生產與發布問題,讓產品和運營同學能夠以所見即所得的方式自主完成靜態頁面制作與發布,進而縮短溝通成本和研發成本。
但是,隨著越來越多業務部門開始咨詢并使用這個平臺,我們后續不斷完善并擴充了很多的功能。經過多次版本的設計和迭代開發后,將該平臺命名為Page-佩奇,并且注冊成為美團內部的公共服務,開始為美團內部更多同學提供更好的使用體驗。
本文將系統地介紹Page-佩奇平臺的定位、設計思路、實現原理及取得成效。我們也希望這些實戰經驗與總結,能給更多同學帶來一些啟發和思考。
我們希望將Page-佩奇打造成一款為產品、運營、開發等用戶提供快速一站式發布網頁的產研工作臺,這是對該平臺的一個定位。
一般來講,傳統開發流程是這樣的:首先產品提出需求,然后召集研發評審,最后研發同學開發并且部署上線;當需求上線之后,如果有問題需要反饋,產品再找研發同學進行溝通并修復,這種開發流程也是目前互聯網公司比較常見的開發流程。
圖2 傳統開發流程圖
而美團Page-佩奇平臺的開發流程是:首先產品同學提出需求,然后自己在Page平臺進行編輯和發布上線,當需求上線之后有問題需要反饋,直接就能觸達到產品同學,他們通常可自行進行修復。如果需求需要定制化,或者需要做一些復雜的邏輯處理,那么再讓研發人員配合在平臺上進行開發并發布上線。
圖3 Page佩奇平臺開發流程圖
簡單來說,對那些功能相對簡單、需求變動比較頻繁的頁面,如果用傳統的開發流程將會增加產研溝通和研發排期成本,因此傳統方案主要適用于功能復雜型的需求。而Page佩奇平臺開發流程,并不適合功能復雜型的需求,特別適用于功能相對簡單、需求變動比較頻繁的頁面需求。
綜上所述,可以看出這兩種開發流程其實起到了一個互補的作用,如果一起使用,既可以減少工作量,又可以達到降本提效的目的。
我們最初設計Page-佩奇平臺的初心其實很簡單,為了給產品和運營提供一個通過富文本編輯器快速制作并發布網頁的工具。但是,在使用的過程中,很多缺陷也就慢慢地開始暴露,大致有下面這些問題:
于是,我們針對這些問題進行了一些思考和調研:
實現一個功能很容易,但是想要實現一個相對完善的功能,就必須好好下功夫,多思考和多調研。于是,圍繞著這些問題,我們不斷挖掘和延伸出了一系列功能:
功能流程圖如下所示:
圖4 Page佩奇平臺功能流程圖
Page-佩奇平臺的基礎服務有四個部分,包括物料服務、編譯服務、產品賦能、擴展服務。
圖5 整體架構圖
3.3.2 核心架構
圖6 核心架構圖
Page-佩奇平臺核心架構主要包含頁面基礎配置層、頁面組裝層以及頁面生成層。我們通過Vuex全局狀態對數據進行維護。
圖7 關鍵流程圖
如上圖7所示,平臺的核心流程主要包含頁面創建之后的頁面預覽、編譯服務、生成頁面。
Page-佩奇平臺也可以作為一個完善的富文本編輯器供業務系統使用,支持內嵌到其他系統內。作為消息發布等功能承載,減少重復的開發工作,同時我們配備完善的SDK供大家選擇使用。通過Page-SDK可以直接觸發Page平臺發布、管理等操作,具體的流程如下圖所示:
圖8 Page-SDK流程圖
在使用Page-佩奇平臺的時候,美團內部一些業務方提出想要通過Page-佩奇平臺進行頁面的發布,同時想要拿到發布的內容做一些自定義的處理。于是,我們提供了Open API開放能力,支持以HTTP和Thrift兩種方式進行調用。下面主要講一下Thrift API實現的思路,首先我們先了解下Thrift整體流程:
圖9 Thrift整體流程圖
Thrift的主要使用過程如下:
那么下面我們具體講下,Node語言是如何實現和其他服務語言實現調用的。由于我們的服務使用的Node語言,因此我們的Node服務就充當了服務端的角色,而其他語言(Java等)調用就充當了客戶端的角色。
圖10 Thrift使用詳細流程圖
目前,美團內部已經有相對成熟的NPM包服務,已經幫我們實現了服務注冊、數據傳輸、服務發現和獲取流程。客戶端如果想調用我們所提供的的Open API開放能力,首先申請AppKey,然后選擇使用Thrift方式或者HTTP的方式,按照所要求的參數進行請求調用。
能力:富文本編輯。 描述:提供富文本可視化編輯,產品和運營無需前端就可以發布和二次編輯頁面。 場景:文本協議,消息通知,產品FAQ。
具體案例:
圖11 H5靜態文本協議案例
能力:開放API(Thirft + HTTP)。 描述:提供開放API,支持業務自定義和樣式渲染到業務系統,同時解決了iframe體驗問題。 場景:客戶端、后端、小程序的同學,可根據API渲染文案,實現動態化管理富文本信息。
具體案例:
小程序使用組件、Vue使用v-html指令實現動態化渲染商品選擇說明。
{
"code": 0,
"data": {
"tag": "蘋果,標準",
"title": "如何挑選蘋果",
"html": "<h1>如何挑選蘋果</h1>><p>以下標準可供消費者參考</p><ul><li>酸甜</li><li>硬度</li></ul>",
"css": "",
"js": "",
"file": {}
},
"msg": "success"
}
能力:WebIDE代碼編輯。 描述:開發基于WebIDE代碼開發工作,基于渠道和環境修改下載鏈接,能夠做到分鐘級支撐。 場景:根據產品創建靜態頁面進行邏輯和樣式開發。
具體案例:
var ua=window.navigator.userAgent
var URL_MAP={
ios: 'https://apps.apple.com/cn/app/xxx',
android: 'xxx.apk',
ios_dpmerchant: 'itms-apps://itunes.apple.com/cn/app/xxx'
}
if (ua.match(/android/i)) location.href=URL_MAP.android
if (ua.match(/(ipad|iphone|ipod).*os\s([\d_]+)/i)) {
if (/xx\/com\.xxx\.xx\.mobile/.test(ua)) {
location.href=URL_MAP.ios_dpmerchant
} else {
location.href=URL_MAP.ios
}
}
能力:WebIDE代碼編輯 + 物料平臺。 描述:通過物料平臺,引入公司客戶端橋SDK,可以快速完成客戶端通信需求。方便前端調試客戶端基礎橋功能。 場景:客戶端跳轉,通信中間頁。
具體案例:
// 業務偽代碼
XXX.ready(()=> {
XXX.sendMessage({
sign: true,
params: {
id: window.URL
}
}, ()=> {
console.error('通信成功')
}, ()=> {
console.error('通信失敗')
})
})
能力:提供膠水層Page-SDK,連接業務系統和Page。 描述:業務系統與Page-佩奇平臺可進行通信,業務系統可調用Page發布、預覽、編輯等功能,Page可返回業務系統頁面鏈接、內容、權限等信息。減少重復前后端工作,提升研發效率。 場景:前端富文本信息渲染,后端富文本信息管理后臺。
具體案例:
圖12 業務系統內嵌Page案例
截止目前數據統計,Page佩奇平臺生成網頁5000多個,編輯頁面次數16000多次,累計頁面訪問PV超過8260萬。現在,美團已經有十多個部門和三十多條業務線接入并使用了Page佩奇平臺。
圖13 Page平臺每日生成頁面統計
富文本編輯器和WebIDE不僅是復雜的系統,而且還是比較熱門的研究方向。特別是在和美團的基建結合之后,能夠解決團隊內部很多效率和質量問題。這套系統還提供了語法智能提示、Diff對比、前置檢測、命令行調試等功能,不僅要關注業務發布出去頁面的穩定性和質量,更要有內置的一系列研發插件,主動幫助研發提高代碼質量,降低不必要的錯誤。
經過長期的技術和業務演進,Page-佩奇平臺已經能夠有效地幫助研發人員大幅提升開發效率,具備初級的Design To Code能力,但是仍有許多業務場景值得去我們探索。我們也期待優秀的你參與進來,一起共同建設。
高瞻、宇立、肖瑩、浩暢,來自美團醫藥終端團隊。王詠、陳文,來自美團閃購終端團隊。
美團醫藥長期招聘Android、iOS、FE前端工程師,坐標在北京和成都。感興趣的同學可將簡歷發送至:sunyuli@meituan.com(郵件主題請注明:美團醫藥終端)。
| 本文系美團技術團隊出品,著作權歸屬美團。歡迎出于分享和交流等非商業目的轉載或使用本文內容,敬請注明“內容轉載自美團技術團隊”。本文未經許可,不得進行商業性轉載或者使用。任何商用行為,請發送郵件至tech@meituan.com申請授權。
天來聊聊定制你的bootstrap文件,換句話說就是修改css文件,大家知道bootstrap文件是有less書寫的,好吧,我們從源頭開始吧!
1、下載bootstrap源文件:
在bootstrap中文網上下載源碼,不要選錯呀。
2、解壓后看到less文件夾,打開,找到variables.less文件,這個文件基本上存的都是變量;用sublime打開。
3、看到了從第4行開始:
4、我們今天就說這一段,基本的less語法,等說完了,再說如何解析成css文件。
第1,2,3行是注釋,在less中,注釋有2中,一種是css的格式注釋,一種是//注釋;
區別1:/**/是多行注釋 // 是單行注釋
區別2:/**/編譯的時候寫入css文件 //注釋的不寫入css文件
@gray-base: #000; 定義一個變量@gray-base值為#000;
@gray-darker: lighten(@gray-base, 13.5%); // #222
定義一個變量@gray-darker 值是多少呢?
@gray-base 是#000;16進制是 0;
0的13.5%,黑色的就是 22呀!
所以 lighten默認的就是#222;
整個是什么意思呢? 就是@gray-darker的值為 lighten(@gray-base, 13.5%);,如果變量沒有傳參數,那么就是默認的222;如果傳了參數,則為(@gray-base, 傳入的參數)
我們在392行,可以看到“反色導航條”顏色定義的代碼:
它的后面傳了個參數為15%,值是多少你自己算算。
這樣的好處是什么呢?
你想想,你在做一個項目的時候,客戶今天說就定這個顏色了,過了幾天,改了,換顏色了!
如果你用css寫的,O(∩_∩)O~,你就挨個改吧。如果你用less寫的,只需要修改一處,就可以了,分分鐘的事!愜意吧!
今天就先說到這里,改天說說如何讓less文件編譯為css文件!
1.安裝node: 官網
2.配置cnpm(非必須)
設置淘寶鏡像:npm install -g cnpm --registry=https://registry.npm.taobao.org,10分鐘同步官網一次。
或者可以使用npm config set registry https://registry.npm.taobao.org來改變npm默認下載地址
1.初始化工程目錄
mkdir my_demo
cd my_demo
npm init
回車會生產一個npm目錄,只包含一個package.json,創建src目錄
mkdir src
2.配置工程
(1)首先安裝webpack和webpack-cli
npm i -D webpack webpack-cli -g
(2)新建并配置webpack(entry, output, loader, plugin, mode)
const path=require('path');
4 module.exports={
5 // 項目入口,webpack從此處開始構建
6 entry: {
7 main: path.join(__dirname, 'src/index.js'), // 指定入口,可以指定多個。參考webpack文檔
8 },
9 output: {
10 path: path.join(__dirname, "dist"), // bundle生成(emit)到哪里
11 filename: "bundle.js", // bundle生成文件的名稱
12 },
13 }
(3)配置開發應用服務器
正常情況下,我們需要以應用服務器打開我們的網頁,webpack-dev-server提供了一個簡單的web服務器,并且能夠實時重新加載。指南 首先需要安裝webpack-dev-server
npm i -D webpack-dev-server
module.exports={
entry: './src/index.js',
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
},
devServer: {
contentBase: './dist',
},
...
}
這段配置告訴webpack-dev-server,在默認host和port(8080)下建立服務,并將contentBase目錄下的目錄,作為可訪問文件。
接下來讓我們的服務器跑起來webpack --mode production --config ./webpack.config.js,在package.json配置命令腳本:
可能會有報錯,webpack-cli @4的版本不兼容,降到@3就可以啦
"scripts" : {
2 "start": "webpack-dev-server --mode development --open",
3 "build: "webpack --mode production --config ./webpack.config.js"
4 }
其中mode為模式,webpack會有相應優化
3.安裝react,babel
安裝react全家桶,安裝jsx轉換工具babel
npm install react react-dom react-router-dom -S
npm install babel-core babel-loader babel-preset-env babel-preset-react -D
配置webpack使其生效
module.exports={
// ...省略
module: {
rules: [
{
test: /\.(js|jsx)$/, // babel7開始支持編譯ts,tsx
exclude: /node_modules/,
enforce: 'pre',
use: [{
loader: 'babel-loader',
}, {
loader: 'eslint-loader', // 指定啟用eslint-loader,需安裝
options: {
formatter: require('eslint-friendly-formatter'), // 安裝
emitWarning: false
}
}]
},
]
}
babel-loader的配置可以在rules中loader option配置,也可以單獨一個.bablerc或babel.config.js的文件放在根目錄下,babel會自動讀取配置
{
"presets": ["env","react"],
// antd按需引入
// "plugins": ["react-hot-loader/babel", ["import", { "libraryName": "antd", "libraryDirectory": "es","style": "css" }], "transform-runtime"]
}
擴展:babel 7.x 以上開始支持兩種類型的配置文件, 分別是.babelrc 和 babel.config.js ,在官方的描述里:
babel.config.js 當前項目維度 (Project Wide)的配置文件,相當于一份全局配置,如果 babel 決定應用這個配置文件,則一定會應用到所有文件的轉換。
.babelrc 相對文件(File Relative)的配置文件, babel 決定一個 js 文件應用哪些配置文件時,會執行如下策略: 如果要轉換的這個 js 文件在當前項目內,則會先遞歸向上搜索最近的一個 .babelrc 文件(直到遇到package.json目錄),將其與全局配置合并。如果這個 js 文件不在當前項目內,則只應用全局配置,忽略與這個文件相關的 .babelrc 。
這兩個我更愿意將其稱為全局配置 (babel.config.js) 和局部配置 (.babelrc) 便于理解一些。
參考鏈接:https://zhuanlan.zhihu.com/p/367724302
4.添加常用loader
(1)樣式loader:
前置安裝style-loader、less-loader/scss-loader、css-loader、postcss-loader。
test: /\.(css|less)$/,
exclude: /node_modules/,
include: /src/,
use: [
{loader: "style-loader"},
{
loader: 'css-loader',
options: {
minimize: process.env.NODE_ENV==='production',
importLoaders: 2,
localIdentName: '[name]-[local]-[hash:base64:5]',
modules:true
}
}, {
loader: 'postcss-loader',
options: { // 如果沒有options這個選項將會報錯 No PostCSS Config found
plugins: (loader)=> [
require('autoprefixer')(), //CSS瀏覽器兼容
]
}
},{
loader: 'less-loader',
options: {
javascriptEnabled: true,
}
}],
(2)圖片資源 file-loader/url-loader
url-loader里的limit參數可以指定小于配置參數的文件轉換成base64編碼的文件,其余的生成一個可配置hash的單獨文件(file-loader的作用),這里也指出了兩者的不同之處。
1.單獨打包樣式文件
# for webpack 4
npm install --save-dev mini-css-extract-plugin
# for webpack 3
npm install --save-dev extract-text-webpack-plugin
# for webpack 2
npm install --save-dev extract-text-webpack-plugin@2.1.2
# for webpack 1
npm install --save-dev extract-text-webpack-plugin@1.0.1
extract-text-webpack-plugin用法
const ExtractTextPlugin=require("extract-text-webpack-plugin");
module.exports={
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
}
]
},
plugins: [
new ExtractTextPlugin("styles.css"),
]
}
mini-css-extract-plugin用法
const MiniCssExtractPlugin=require('mini-css-extract-plugin');
module.exports={
plugins: [new MiniCssExtractPlugin()],
module: {
rules: [
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
],
},
};
2.為打包后的文件增加hash
如果瀏覽器加載發現遠端文件沒有發生變化時,將會啟用緩存,導致新修改的頁面并沒有同步,這時候為了避免緩存,我們就需要讓每次打包后的文件有不同的文件名,以減少緩存。
// webpack.config.js -> output
output: {
path: path.join(__dirname, "dist"),
publicPath: '/',
filename: "js/[name]-[hash]" + ".js",
chunkFilename: "js/[name]-[hash]" + ".js",
},
同樣抽取的css文件也可以類似命名hash
3.html-webpack-plugin
生成一個自動引入我們生成的文件的新模板。 當我們在生成有 hash 標識符的 css,js時非常方便
4.提取公共代碼
splitChunks:提取公共代碼,防止代碼被重復打包,拆分過大的js文件,合并零散的js文件。
具體使用翻看專題
*請認真填寫需求信息,我們會在24小時內與您取得聯系。