自打出生的那一天起,Weex 就免不了被拿来同 React Native「一决高下」的命运。React Native 宣称「Learn Once, Write Anywhere」,而 Weex 宣称「Write Once, Run Everywhere」。在我看来,并没有谁更好,只有谁更合适。下面我将围绕 Weex 入门进行讲解。
(如果你尚不了解 React Native,并想简单入门,可以阅读 【整理】React Native 快速入门笔记)
什么都不说,先给你感受下 Weex 的效果。以下就是我使用 Weex,4*8h(不连续)做出来的 demo,其中还包括素材收集,踩坑总结等时间。
此处是 demo 源码:
https://github.com/zwwill/yanxuan-weex-demo
不得不说,使用 Weex 开发 app 对于我们纯前端人员来说,是件「很爽」的事情,只要你熟悉了他的语法,基本可以做到一周上手写 app。极其适合交互要求不高,时间紧迫,人手不足的同构开发需求。
但是,当然有但是,如果你想写出一个完美的 app,你就需要在性能优化上下很大的功夫,包括动画的优化,过场的优化,图片的优化,细节的打磨等等,再者,就是你需要掌握或者「能写」一些原生的代码,不然有些功能你是实现不了的,比如 status bar 的属性更改,开场动画的制作,内存的回收,webview 的监听等等。
下面我们具体讲讲入门知识
Weex 提供了多端一致的技术方案。
在同构这条路上,Weex 比 React Native做得更彻底,他「几乎」做到了,「你来使用 vue 写一个webapp,我顺便给你编译成了 ios 和 android 的原生 app」
至于为什么要造这个轮子,官方给了以下说法
1、今天在技术社区有大量的 web 开发者,Weex 可以赋能更多的 web 开发者构建高性能和高体验的移动应用。
2、Web 开发本身具有非常强的高效率和灵活性,这和 Weex 想解决的移动端动态性问题不谋而合。
3、Web 标准和开发体验是很多顶尖而优秀的科技公司共同讨论和建设的结果,本身的设计和理念都有极高的品质保障
4、同时 Weex 也希望可以借此机会努力为标准贡献一点自己的微薄之力。
5、Web 是一种标准化的技术,标准本身就是一种力量,基于标准、尊重标准、贴近标准都意味着拥有更多的可能性。
6、Web 今天的生态和社区是非常繁荣的,有很多成熟的工具、库、工程体系、最佳实践可以使用、引入和借鉴。
在我看来,Weex 其实是 Alibaba 团队提高生产效率的产物,在淘宝这类要求多端统一迭代快速的部门,三端约定一种便于统一的规范,在加上时间的发酵,渐渐的就有了此类脚手架的雏形,同时在脸书 React Native 开源带来的极大轰动后,自己也坐不住了吧^_^
好了,闲话就说到这,下面就来让我们解剖一下WEEX的优劣良莠。
入门 Weex 前需要了解以下知识,这样能帮助你更快的掌握
Node:《Node.js 教程》
Vue:《Vue.js官方教程》
ES6:《ECMAScript 6 入门》
再者就是 ios 和 android 开发语法的入门和编辑器的使用
IOS : MacOS
, 黑苹果
Android :MacOS
, Linux
, Windows
你可以参考官方文档安装必须的依赖环境 http://weex.apache.org/cn/guide/set-up-env.html,
也可以直接安装以下环境
安装 Xcode IDE 和 Xcode 的命令行工具(IOS 开发依赖)
下载必须的插件:
a) JDK1.8+
b) Show Package Details
c) Android SDK Build Tools
d) Android Support Repository
配置基础环境:
a) ANDROID_HOME (如运行是遇到问题可参考此文 http://www.jianshu.com/p/a77396301b22)
b) JAVA_HOME
官方文档上的入门 Hello world 是 web 端的,紧接着介绍了如何「集成 Weex 到已有应用」
但是,身为一个 web 前端开发者,如果你不懂原生语音的话,介绍这些并不能起到很好的引导作用,因为web前端开发者都有「一统前端界」的野心(Web+Android+IOS),「寄人篱下」只能是暂时的。
快速创建并运行一个纯 Weex App 对于「纯」前端同学来说,才是有意思的事儿。
但:
为什么文档要这么设计也是跟Weex的定位有关的,读完下文后续你就慢慢懂了,后面我将做总结解释
如果你在官方教程里没有找到创建工程的教程,可以阅读此文《Weex 快速创建工程 Hello World》
Weex 在迭代的过程中选择了于 Vue 2.0 握手,因为该版本的 Vue 加入了 Virtual-DOM 和预编译器的设计,使得该框架在运行时能够脱离 HTML 和 CSS 解析,只依赖 JavaScript,如此,Vue 在和 Weex 合作后,便获得了使用 JS 预编译原生的组件 UI 的能力。
同 React Native 一样,有人也将 Weex 叫做 Vue Native。
如果你对 Vue 还不了解,可以先学习【预科】部分推荐的《Vue.js 官方教程》。
那么接下来我们讲讲,Vue 在 Weex 中的不同
虽说 Weex 使用 Vue 语言写的,但毕竟是需要在不同平台间运行的,虽然大部分语法都有支持,但是依然有部分语法是不同的
目前 Weex 支持了基本的容器 (div)、文本 (text)、图片 (image)、视频 (video) 等组件,注意是组件,而不是标签,虽然使用起来跟 html 标签很像,至于其他标签基本可以使用以上组件组合而成。
因为 Weex 解析 vue 得到的并不是 dom,而是原生布局树
并不支持 Web 中所有的事件类型,详情请参考《通用事件》
在 Weex 中能够调用移动设备原生 API,使用方法是通过注册、调用模块来实现。其中有一些模块是 Weex 内置的,如 clipboard 、 navigator 、storage 等。
《clipboard 剪切板》
《navigator 导航控制》
《storage 本地存储 》
为了保持框架的通用性,Weex 内置的原生模块有限,不过 Weex 提供了横向扩展的能力,可以扩展原生模块,具体的扩展方法请参考《iOS 扩展》 和《Android 扩展》。
Weex 中的样式是由原生渲染器解析的,出于性能和功能复杂度的考虑,Weex 对 CSS 的特性做了一些取舍
1、Weex 中只支持单个类名选择器,不支持关系选择器,也不支持属性选择器。
2、组件级别的作用域,为了保持 web 和 Native 的一致性,需要写法
3、支持了基本的盒模型和 flexbox 布局,详情可参考Weex 通用样式文档。但是需要注意的是,
display: none;
可用opacity: 0;
代替,(opacity<=0.01时,元素可点透)举个栗子,以下是严选App Demo首页的简化代码
<template>
<div class="wrapper">
<text class="iconfont">text>
<home-header>home-header>
<scroller class="main-list" offset-accuracy="300px">
<refresher>refresher>
<div class="cell-button" @click="jumpWeb('https://m.you.163.com')">
<yx-slider :imageList="YXBanners" >yx-slider>
div>
<div class="cell-button">
<block-1 :title="block1.title" :items="block1.items">block-1>
div>
scroller>
div>
template>
<style scoped>
.iconfont { font-family:iconfont; }
.main-list{ position: fixed; top: 168px; bottom: 90px; left: 0; right: 0; }
style>
<script>
var navigator = weex.requireModule('navigator');
import util from '../../src/assets/util';
import Header from '../components/Header.vue';
import refresher from '../components/refresh.vue';
import YXSlider from '../components/YXSlider.vue';
import Block1 from '../components/Block1.vue';
export default {
components: {
'home-header': Header,
'refresher': refresher,
'yx-slider': YXSlider,
'block-1': Block1
},
data () {
return {
YXBanners: [
{ title: '', src: 'http://doc.zwwill.com/yanxuan/imgs/banner-1.jpg'},
{ title: '', src: 'http://doc.zwwill.com/yanxuan/imgs/banner-2.jpg'},
{ title: '', src: 'http://doc.zwwill.com/yanxuan/imgs/banner-3.jpg'}
]
}
},
methods: {
jumpWeb (_url) {
const url = this.$getConfig().bundleUrl;
navigator.push({
url: util.setBundleUrl(url, 'page/web.js?weburl='+_url) ,
animated: "true"
});
}
}
}
script>
如果以上代码脱离工程单独出现,基本上是无法得知他是 Weex 工程。此处可切实感受到 Weex 的 web 开发体验
<template>
<div>
<text v-for="(v, i) in list" class="text">{{v}}text>
<image style="" src="">image>
<video class="video" :src="src" autoplay controls @start="onstart" @pause="onpause" @finish="onfinish" @fail="onfail">video>
div>
template>
Weex 工程中常用的标签有,
,
,(组件另算),由此四种标签基本可以满足绝大多数场景的需求,虽说此标签同 web 工程下的标签用法一致,但此处的标签已不再是我们前端口中常提的 html 标签,而且名存实亡的 Weex 标签,确切讲是 Weex 组件。
通过weex-loader、vue-loader、weex-vue-render的解析最终转换输出的便是实际的组件,有此设计只是为了完成「web开发体验」的目标。但是我们身为上层的开发人员要清楚自己每天「把玩」的到底是个什么「鬼」。
其实用阉割版来形容 Weex 的 css 支持度并不合适,但如果从「web开发体验」的角度来衡量,那么这个形容词也是可以理解的。(此处对 Weex 寄有厚望^_^)
Weex 中的所有 css 属性值的单位均为 px
,也可省略不写,系统会默认为 px
单位。
Weex 中只支持单个类名选择器,不支持关系选择器,也不支持属性选择器。
/* 支持单个类名选择器 */
.one-class {
font-size: 36px;
}
/* 不支持关系选择器 */
.parent > .child {
padding-top: 10px;
}
/* 不支持属性选择器,不支持 `v-cloak` 指令 */
[v-cloak] {
color: #FF6600;
}
这个只是对样式定义的限制,不影响样式类名的使用,在标签中可以添加多个样式类名,如:
<template>
<div class="one two three"><div>
template>
weex支持css基本的盒模型结构,但需要注意的是
box-sizing
属性值默认为 border-box
margin
,padding
,border
等属性暂不支持合并简写Weex 中对 flexbox 布局支持度很高,但依然有部分属性并不支持,如 align-items:baseline;
、align-content:space-around;
、align-self:wrap_reverse;
等。
具体 Weex 对 flexbox 的支持和布局算法,可通过此文进行了解由 FlexBox 算法强力驱动的 Weex 布局引擎,此处便不再赘述。
在 Weex 的 ios 和 android 端,并不支持 display
属性。
因此,不能使用 display:none;
来控制元素的显隐性,所以 vue 语法中的 v-show
条件渲染是不生效的。
我们可以使用 v-if
代替,或者用 opacity:0;
来模拟。
需要注意的是,ios和android端并不能使用 opacity:0;
来完全模拟 visibility: hidden;
,因为,当
opacity 的只小于等于 0.01 时,native 控件便会消失,占位空间还在,但用户无法进行交互操作,点击时会发生点透效果。
Weex 支持 css3 属性,虽然支持并不够,但相较 React Native 的「不能用」已经是强大很多了。
以下几种属性我们在开发前需要知道她的支持度
animation
实现动画交互由于使用了增强版的 webpak 打包工具 weexpack,支持第三方框架也是件自然而然的事情。
常用的有 vuex
、vue-router
等,可根据项目实际情况引入需要的第三方工具库
npm 包管理是前端开发朋友们再熟悉不过的包管理方式了。这也是为什么 React Native 和 Weex 都选择这种管理方式的原因。
以下是本工程的 package.json 文件,这里就不做讲解了,不熟悉的朋友点这里->NPM 使用介绍
{
"name": "yanxuan-weex",
"version": "1.0.0",
"description": "a weex project",
"main": "index.js",
"scripts": {
"build": "webpack",
"build_plugin": "webpack --config ./tools/webpack.config.plugin.js --color",
"dev": "weex-builder src dist -w",
"serve": "webpack-dev-server --config webpack.dev.js -p --open"
},
"keywords": ["weex"],
"author": "zwwill",
"license": "MIT",
"dependencies": {
"vue": "^2.4.2",
"vue-router": "^2.7.0",
"vuex": "^2.1.1",
"vuex-router-sync": "^4.3.0",
"weex-html5": "^0.4.1",
"weex-vue-render": "^0.11.2"
},
"devDependencies": {
"babel-core": "^6.21.0",
"babel-loader": "^6.2.4",
"babel-plugin-add-module-exports": "^0.2.1",
"babel-plugin-transform-runtime": "^6.9.0",
"babel-preset-es2015": "^6.9.0",
"babel-runtime": "^6.9.2",
"css-loader": "^0.26.1",
"history": "^4.7.2",
"quick-local-ip": "^1.0.7",
"vue-loader": "^13.0.4",
"vue-template-compiler": "^2.4.2",
"webpack": "^2.7.0",
"webpack-dev-server": "^2.4.2",
"weex-builder": "^0.2.7",
"weex-loader": "^0.4.5",
"weex-router": "0.0.1"
}
}
Weex 容器默认的显示宽度 (viewport) 是 750px,页面中的所有组件都会以 750px 作为满屏宽度。
这很像移动设备的逻辑像,比如 iPhone 6 的物理像素宽为 750,逻辑像素
Type | iPhone 3G | iPhone 4 | iPhone 6 | iPhone 6Plus | |
---|---|---|---|---|---|
物理像素 | 320x480 | 640x960 | 750x1134 | 1080x1920 | |
逻辑像素 | 320x480 | 320x480 | 375x667 | 414x736 | |
像素比 | @1x | @2x | @2x | @3x |
类比在 Weex 中,如果所有的显示宽度都是用默认值 750,那么显示出来的实际像素信息为
Type | iPhone 3G | iPhone 4 | iPhone 6 | iPhone 6Plus | |
---|---|---|---|---|---|
物理像素 | 320x480 | 640x960 | 750x1134 | 1080x1920 | |
显示像素 | 750x1125 | 750x1125 | 750x1134 | 750x1333 | |
像素比 | @0.427x | @0.85x | @1x | @1.44x |
所以我们在使用 Weex 做 UI 适配时就没有所谓的 @2x 图和 @3x 图,所有的尺寸都是Weex帮我们根据
750 作为基数宽做的缩放。
当然,Weex 提供了改变此显示宽度的 API,setViewport
,通过此方法可以改变页面的显示宽度,可以实现每个页面根据自己的需求改变基数逻辑尺寸
因此对于一些固定的 icon,不建议使用普通的静态图片或者雪碧图,这里建议使用矢量的字体图片,有以下优点:
Weex 的调试方式有多种,如果说RN的调试模式是解放了原生开发的调试,那么 Weex 的调试方式可以说是赋予了 web 模式调试原生应用的能力。
此方法多用于解决 bug,检测控件的布局问题
# 调试单个页面
$ weex debug your_weex.vue
# 调试整个工程
$weex debug your/path -e App.vue
执行调试命令后,会将指定的文件打包成 JSBundle,并启动一个 weex Devtool 服务(http://localhost:8088可访问,如下图),同时将 JSBundle 文件传递至该服务跟路径下的weex文件夹内(http://localhost:8088/weex/App.js,实际是下图右边二维码的的内容)。
使用 Weex Playground App 扫下左二维码进入调试模,见下图
再次扫码右方二维码,点击【inspector】即可进入调试模式。
每一个控件都是相同的数据结构
<view class="WXText" frame="{{0,0},{414,736}}" hidden="NO" alpha="1" opaque="YES">view>
此方法多用于开发调试,试试观察结果
$ weex your_weex.vue
如果出现 access 权限报错,使用管理员指令
$ sudo weex your_weex.vue
此时本地同时启动一个watch的服务器用于检查代码变更,自动重新构建 JSBundle,视觉同步刷新。
上图看到的效果即为H5页面的效果,我们一般在整个单页编写完成后在使用 Weex Playground App 扫码查看真机效果,或者你也可以在编写的同时使用真机观察代码的运行效果,每次重新构建包到重绘的速度还是很快的。
但前提是你要保证,你的手机和电脑的连在同一个局域网下,并且使用IP访问。
虽然说,Weex 可以抹平三端开发的差异,但是知其然也应知其所以然使用起来才能游刃有余。
熟悉 React Native 的人都知道, React Native 的发布实际上就是发布一个 JSBundle,Weex 也是这样,但不同的是,Weex 将工程进行分包,发布多个 JSBundle。因为 Weex 是单页独立开发的,每个页面都将通过 Weex 打包器将 vue/we 页面打包成一个单独的 JSBundle,这样的好处在于减少单个 bundle 包的大小,使其变的足够小巧轻量,提高增量更新的效率。
# 仅打包
$ npm run build
# 打包+构建
$ weex build ios
# 打包+构建+安装执行
$ weex run ios
以上三种均会触发 Weex 对工程进行打包。
在我们执行了以上打包命令后,所有的工程文件将被单独打成一个独立的 JSBundle,如下:
打包后的 JSBundle 有两种格式
# 由.vue文件打包出来的包格式(简写),使用 vue 2.0 语法编写
// { "framework": "Vue"}
/******/ (function(modules) {
.......
/******/ })
# 由.we文件打包出来的包格式(简写),使用 weex 语法编写
// { "framework": "Weex" }
/******/ (function(modules) {
.......
/******/ })
不同的头部是要告诉使用什么语法解析此JSBundle。
至此,我们准备「热更新的包」就已经准备完毕了,接下就是发包执行了。
打包后的 JSBundle 一般发布到发包服务器上,客户端从服务器更新包后即可在下次启动执行新的版本,而无需重新下载 app,因为运行依赖的 WeexSDK 已经存在于客户端了,除非新包依赖于新的 SDK,这也是热更新的基本原理。
【WeexSDK】包括
- 【JS Framework】JSBundle 的执行环境
- 【JS-Native Bridge】中间件或者叫通讯桥梁,也叫【Weex Runtime】
- 【Native Render Engine】解析 js 端发出的指令做原生控件布局渲染
Weex 的 iOS 和 Android 客户端的【JSFramework】中都会运行一个 JavaScript 引擎,来执行 JS bundle,同时向各端的渲染层发送规范化的指令,调度客户端的渲染和其它各种能力。iOS 下选择了 JavaScriptCore 内核,而在 Android 下选择了 UC 提供的 v8 内核(RN两端都是JavaScriptCore 内核)。
JSBundle 被 push 到客户端后就会在 JSFramework 中执行,最终输出三端可读性的 VNode 节点,数据结构简化如下:
{
tag: 'div',
data: {
staticStyle: { justifyContent: 'center' }
},
children: [{
tag: 'text',
data: {
staticClass: 'txt'
},
context: {
$options: {
style: {
freestyle: {
textAlign: 'center',
fontSize: 200
}
}
}
},
children: [{
tag: '',
text: '文字'
}]
}]
}
有了统一的 VNode 节点,各端即可根据自己的方法解析渲染原生UI了,之前的所有操作都是一致的,包括文件格式、打包编译过程、模板指令、组件的生命周期、数据绑定等。
然而由于目标执行环境不同(浏览器和 Weex 容器),在渲染真实原生 UI 的时候调用的接口也不同。
此过程发生在【Weex SDK】的【Weex Runtime】中。
最总【Weex Runtime】发起渲染指令callNative({...})
有RenderEngine完成渲染
官方配图:
扩充配图:
目前支持单页使用或整个 App 使用 Weex 开发(还不完善,需要开发 Router 和生命周期管理)。
本文先行的严选 demo 便是使用第二种全屏模式,使用 Weex 开发整个 App,期间触碰到 Weex 的在此模式下诸多不足,如 StatusBar 控制、Tab 切换、开场动画自定义、3DTouch、 Widget 等等原生的特色功能没有现成的 API,需要我们自己扩展,甚至扩展不了。因此并不能完全“灭掉”原生。
所以,目前在阿里内部使用较多的是此模式中的单页模式,这也是为什么官方文档在介绍原理后就直接奔入集成到原生应用的主题上去了。
把 Weex 当作一个 iOS/Android 组件来使用,类比 ImageView。这类需求遍布手淘主链路,如首页、主搜结果、交易组件化等,这类 Native 页面主体已经很稳定,但是局部动态化需求旺盛导致频繁发版,解决这类问题也是 Weex 的重点。
在 H5 种使用 Weex,类比 WVC。一些较复杂或特殊的 H5 页面短期内无法完全转为 Weex 全页模式(或RN),比如互动类页面、一些复杂频道页等。这个痛点的解决办法是:在现有的H5页面上做微调,引入Native 解决长列表内存暴增、滚动不流畅、动画/手势体验差等问题。
另外,WVC 将会融入到 Weex 中,成为 Weex 的 H5 Components 模式。
由于 Weex 没有封装 Tab 的组件,因此笔者使用了很多方法来实现Tab切换的功能。
1、vue-router:router 思想方便管理,但是每次切换都是新的实例,没有tab模式
2、opacity、visablity:此处需要注意,Weex的渲染机制和web是有区别的,对夫层设置 opacity 或者visiablity隐藏是无法同时隐藏定位为position:fixed;
的子元素。
3、position、transform:改变 tab 层的位置,此方法在定位为 position:fixed;
的子元素上依然无效。
Weex 中所有的静态资源基本都是网络资源,包括图片、字体图片等,所以使用 iconfont 图标是再合适不过的了。
此 demo 中所有的 icon 均使用 的iconfont。
此处强烈推荐一个站点 www.iconfont.cn。
在此平台你可以找到几乎所有你需要的 icon,你也可以上传自己的 icon 到自己创建的项目中。同时该系统还提供生成ttf、woff 资源,并且做了 cdn 加速和 gzip 压缩,是不是跟 Weex很配呢?
不过也有风险,就是,如果哪天阿里不在维护并回收该平台的资源了,你的 app 可能就会变成这样,全是方框,或者 padding 掉你 H5 的页面
当然,这种及情况出现的几率很小,如果你是一个大公司,你手上有更好的资源急速方案,那就自己保存吧。
UIWebView是我们开发App常用的一个控件,不过Weex帮我们封装好的API明显时不够用的,目前只有pagestart
、pagefinish
、error
,并没有封装像RN那样的onShouldStartLoadWithRequest
拦截地址请求的API,在我看来,这有些不合理,并不清楚轮子的制造者是什么意图。
性能是一个大课题,在此就不做展开了,只稍微提及一些我们开发需要注意的几点
脚本语言天生自带“热更新”,Weex 针对 React Native 的热更新策略做了优化,将 WeexSDK 事先绑到了客户端上,并且对 JSBundle 进行分包增量更新,大大提高了热更新的效率。
但优点也是缺点,如果新包依赖于心的 SDK,此情况下,我们需要发布还有新 SDK 的 app 到应用市场,用户也须从市场更新此 app。不够随着 WeexSDK 版本的稳定后,相信此策略的优势就会凸显出来。
Weex 是一种轻量级、可扩展、高性能框架。集成也很方便,可以直接在 HTML5 页面嵌入,也可嵌在原生UI中。由于和 React Native 一样,都会调用 Native 端的原生控件,所以在性能上比 Hybrid 高出一个层次。
虽说这是一个大胆的实践,但对于大前端社区的统一有着推动作用,显然阿里在这一方面已经迈出了第一步。基本解决了三端同等需求导致资源浪费的痛点。
但后期可能会出现这种现象,开发一个三端的 App 会从原来的个人变成四个人,多出来的那一个人负责开发 Weex 单页。
意思就是,三端统一的不够彻底,但就目前的环境下,这一句是最优方案了,却是提高了开发效率。大前端将来将如何一统三国我们且行且观望吧。
对于一些交互视觉统一且没有很大的性能需求的游戏,Weex 还是可以胜任的。
近期笔者将尝试发布一款纯Weex构建的益智小游戏,敬请期待。
朋友们可以用这个demo体验下 Weex 版扫雷游戏开发
虽然说大一统事件百利的事,但并非无一害。
对于一些有差异化完美体验追求的项目就只能收敛或者放弃了。
对于三端同时上线,一端存在 bug 的情况,Weex 并不能保证做到牵一发而不动全身。
比如安卓的波纹按钮、3DTouch、 Widget、iWatch版本等,目前这些功能还是没有的,不知道以后 Weex
是否将其加入到官方文档中。
以上均为个人见解,不代表官方。如有不当之处还望指正。
[ 1 ] Weex官方文档 - http://weex.apache.org/cn/references/
[ 2 ] 场景研读 - Native 性能稳定性极致优化 - https://yq.aliyun.com/articles/69005
[ 3 ] 门柳 - 详解 Weex JS Framework 的编译过程 - https://yq.aliyun.com/articles/59935?spm=5176.8067842.tagmain.66.1QA1fL
[ 4 ] 阿里百川 - 深度揭秘阿里移动端高性能动态化方案Weex - https://segmentfault.com/a/1190000005031818
[ 5 ] 一缕殇流化隐半边冰霜 - Weex 是如何在 iOS 客户端上跑起来的 - http://www.jianshu.com/p/41cde2c62b81
转载请标明出处
作者: 木羽 zwwill
首发地址: https://github.com/zwwill/blo...