普通视图
2025年度电影总票房破480亿,贺岁档超50部佳片来袭
全国首个儿童身心健康脑机临床研究中心在津建立
电影《疯狂动物城2》票房破17亿
长期限大额存单“缺货”,储户转战转让市场寻宝
全国海水淡化产业联盟在津成立
寻找 Calphalon Commercial:完美锅具是否真实存在?
业内人士:我国近9成老年人倾向于居家养老,潜在护理人才需求或超1000万
海南自贸港首个船东互保组织成立
## React Native 中的 dp、dpi 和分辨率,到底是什么关系?
一、三个概念:px、dpi、dp
-
px:物理像素 / 分辨率里的那个像素 当我们说“720p、1080p”时,其实说的是分辨率:
- 720p ≈ 1280 × 720 像素
- 1080p ≈ 1920 × 1080 像素 这里的“1280、1920、720、1080”,都是 px(物理像素点的个数)。
-
dpi:像素密度(每英寸多少个像素) dpi(dots per inch)表示:一英寸(2.54cm)长度上有多少个像素点。
- 160dpi:每英寸约 160 个像素
- 320dpi:每英寸约 320 个像素(比 160dpi 更细腻) dpi 是硬件属性,跟屏幕多大、分辨率多少一起决定“看起来多清晰”。
-
dp:密度无关像素(React Native 用的逻辑单位) dp(density-independent pixel)是系统定义的一种“逻辑长度单位”,让同样的数值在不同 dpi 的设备上,看起来差不多大。 在 React Native 里,你写
width: 100,这个 100 本质上就是 100dp,而不是 100px。
二、dp 和 dpi 的数学关系
Android / React Native 的约定:
- 把 160dpi 的屏幕当成基准屏幕。
- 在 160dpi 屏幕上:
1dp ≈ 1px - 在其它密度屏幕上,系统用这个公式换算:
px = dp × (dpi / 160)
举几个数字例子:
-
160dpi(基准)
1dp = 1 × (160 / 160) = 1px100dp = 100px -
320dpi
1dp = 1 × (320 / 160) = 2px100dp = 200px -
480dpi
1dp = 1 × (480 / 160) = 3px100dp = 300px(四舍五入后近似)
也就是说:
当你在代码里写 width: 100 时,这个 100 实际上是 100dp。系统会根据当前设备的 dpi,把它换算成应该使用多少个物理像素:
- 在 160dpi 的屏幕上,大约使用 100px(1dp ≈ 1px);
- 在 320dpi 的屏幕上,大约使用 200px(1dp ≈ 2px);
- 在 480dpi 的屏幕上,大约使用 300px(1dp ≈ 3px)。
随着屏幕像素密度提高,单个像素本身变得更小,所以虽然不同设备实际使用的物理像素数量不一样,但这个“100dp 宽”的控件在现实世界里的物理尺寸(比如看起来有多宽、占多大一块屏幕)会尽量保持接近。
现在,160/320/480dpi 和 dp→px 的关系就理清楚了,也解释了“为什么同样的 dp 在不同手机上看起来差不多大”。
三、示意图:同样 100dp 的按钮,在不同 dpi / 分辨率上长什么样?
1)左边:720p,160dpi
- 分辨率:1280 × 720 px
- dpi:160
- 换算:
1dp = 1px,100dp = 100px - 按钮在屏幕上大约占据“某个具体宽度 A 厘米”。
2)中间:1080p,320dpi
- 分辨率:1920 × 1080 px
- dpi:320(像素更密)
- 换算:
1dp = 2px,100dp = 200px - 按钮用更多像素画出来,但因为像素更小,实际占据的物理宽度依然接近 A 厘米。
3)右边:更高分辨率,2K,480dpi
- 分辨率:例如 2560 × 1440 px(举例)
- dpi:480
- 换算:
1dp = 3px,100dp = 300px - 按钮在屏幕上依然大约是 A 厘米宽,只是边缘、文字更细腻。
图中三个按钮,看起来差不多宽,但右边那台的像素数量最多——这就是“dp + dpi 换算”的效果:同样的 dp 值,在高密度屏上用更多 px 去画,从而保持视觉尺寸接近。
四、那 720p、 1080p 在这个故事里扮演什么角色?
- 720p、1080p 说明的是总像素数(分辨率),即屏幕一共多少个 px。
- dpi 说明的是这些像素在物理尺寸上的“密度”,和“屏幕有多大”一起决定屏幕看起来多清晰。
- dp 是在 React Native / Android / iOS 布局里用的“逻辑尺寸单位”,用来写 UI 尺寸。系统会根据当前设备的 dpi 和分辨率,用公式把 dp 换成 px。
你可以大致这样理解:
- px:砖头总数(分辨率)
- dpi:每 1cm 摆多少块砖(密度)
- dp:设计图上画的“这堵墙要 2 米宽”,具体到现场要用多少砖,由工人(系统)根据砖尺寸(dpi)来算。
五、在 React Native 里怎么用 dp 写尺寸?
在 RN 代码里,你不用写 dp、px 这样的单位,直接写数字即可,这个数字默认就是 dp:
// 宽高都是 dp 单位
<View style={{ width: 100, height: 40, borderRadius: 8 }} />
React Native 会做几件事:
- 获取当前设备的:
- 分辨率(总px数)
- 屏幕尺寸(英寸)
- 像素密度dpi(ppi)
- 用公式 px = dp × (dpi / 160),把你的dp换算成真实px
- 把这个px告诉底层原生视图去渲染
你也可以用 Dimensions 和 PixelRatio 看一下当前设备的逻辑尺寸和像素比:
import { Dimensions, PixelRatio } from 'react-native';
const { width, height } = Dimensions.get('window'); // 宽高,单位是 dp
const pixelRatio = PixelRatio.get(); // 设备像素比,约等于 dpi / 160
比如某台设备:
- width = 375,height = 812(单位是逻辑尺寸dp)
- pixelRatio = 3
- 分辨率大约是:宽 375 × 3 = 1125px,高 812 × 3 ≈ 2436px(类似iPhoneX)
六、和设计稿的连接:从px到dp
最后再把“设计稿”这块挂上来,形成闭环:
- 设计稿给的一般是px,比如 750 × 1334。
- 我们约定它对应某个“逻辑宽度”(比如375dp,即2倍图)。
- 换算关系:RN中 dp = 设计稿px / 2(以750px为例)。
西藏人工智能从场景应用走向藏语大模型研发
uni-app开发app之前提须知(IOS/安卓)
大家好,我是鱼樱!!!
关注公众号【鱼樱AI实验室】持续分享更多前端和AI辅助前端编码新知识~~
不定时写点笔记写点生活~写点前端经验。
在当前环境下,纯前端开发者可以通过技术深化、横向扩展、切入新兴领域(MCP TOOLS AI应用产品方向 等)以及产品化思维找到突破口。(不管写多久的代码,技术深度广度如何一定要具备独立赚钱的能力呀!!老铁们,因为总有一天你不在职场总有一天会面对选择和职场的无情)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
前端最卷的开发语言一点不为过,三天一小更,五天一大更。。。一年一个框架升级~=嗯,要的就是这样感觉!与时俱进~ 接下来会逐步分享uni-app的一些开发经验。从文档到基础到实战以及遇到的问题~时间节点不限。
最近依旧比较忙,技术文章还是会不定时更新一些核心的技术分享,和大家一起学习前端路程。
前言须知
uni-app App 端内置了一个基于 weex 改进的原生渲染引擎,提供了原生渲染能力,在 App 端,如果使用 vue 页面,则使用 webview 渲染;如果使用 nvue 页面(native vue 的缩写),则使用原生渲染。一个 App 中可以同时使用两种页面,比如首页使用 nvue,二级页使用 vue 页面
虽然 nvue 也可以多端编译,输出 H5 和小程序,但 nvue 的 css 写法受限,所以如果你不开发 App,那么不需要使用 nvue
nvue 的组件和 API 写法与 vue 页面一致
如果你熟悉 weex 或 react native 开发,那么 nvue 是你的更优选择,能切实提升你的开发效率,降低成本
如果你是 web 前端,不熟悉原生排版,那么建议你仍然以使用 vue 页面为主,在 App 端某些 vue 页面表现不佳的场景下使用 nvue 作为强化补充。
uni-app 在 App 端,支持 vue 页面和 nvue 页面混搭、互相跳转。也支持纯 nvue 原生渲染。
启用纯原生渲染模式,可以减少 App 端的包体积、减少使用时的内存占用。因为 webview 渲染模式的相关模块将被移除。
在 manifest.json 源码视图的"app-plus"下配置"renderer":"native",即代表 App 端启用纯原生渲染模式。此时 pages.json 注册的 vue 页面将被忽略,vue 组件也将被原生渲染引擎来渲染。
如果不指定该值,默认是不启动纯原生渲染的。
// manifest.json
{
// ...
// App平台特有配置
"app-plus": {
"renderer": "native", //App端纯原生渲染模式
}
}
编译模式
weex 编译模式和 uni-app 编译模式(推荐)
weex 的组件和 JS API,与 uni-app 不同。uni-app 与微信小程序相同 主要介绍 uni-app 方式 weex自行了解
![]()
在 manifest.json 中修改 2 种编译模式,manifest.json -> app-plus -> nvueCompiler 切换编译模式。
nvueCompiler 有两个值:
- weex
- uni-app
// manifest.json
{
// ...
// App平台特有配置
"app-plus": {
"nvueCompiler":"uni-app" //是否启用 uni-app 模式
}
}
原生开发没有页面滚动的概念,页面内容高过屏幕高度时,内容并不会自动滚动;只有将页面内容放在list、waterfall、scroll-view/scroller这几个组件下内容才可滚动。这不符合前端开发的习惯,所以在 nvue 编译为 uni-app模式时,uni-app框架会给 nvue 页面外层自动嵌套一个 scroller,从而实现页面内容的自动滚动。
注意:
-
uni-app框架仅对 nvue 页面嵌套scroller容器,不会给组件自动套scroller容器; - 若 nvue 页面有
recycle-list组件时,uni-app框架也不会自动给页面嵌套scroller容器 - 若你不希望自动嵌套
scroller容器,可在pages.json中通过如下配置进行关闭:
{
"path": "",
"style": {
"disableScroll": true // 不嵌套 scroller
}
}
HBuilderX 3.1.0+ 开始支持新的样式编译模式
- weex 编译模式:老模式,样式支持与普通 weex 相同
- uni-app 编译模式:新模式,在 weex 原有样式基础上支持组合选择器(相邻兄弟选择器、普通兄弟选择器、子选择器、后代选择器)详见
// manifest.json
{
// ...
// App平台特有配置
"app-plus": {
"nvueStyleCompiler": "uni-app"
}
}
快速上手
在 HBuilderX 的 uni-app 项目中,新建页面,弹出界面右上角可以选择是建立vue页面还是nvue页面,或者 2 个同时建
不管是 vue 页面还是 nvue 页面,都需要在pages.json中注册。如果在 HBuilderX 中新建页面是会自动注册的,如果使用其他编辑器,则需要自行在 pages.json 里注册。
如果一个页面路由下同时有 vue 页面和 nvue 页面,即出现同名的 vue 和 nvue 文件。那么在 App 端,会仅使用 nvue 页面,同名的 vue 文件将不会被编译到 App 端。而在非 App 端,会优先使用 vue 页面。
如果不同名,只有 nvue 页面,则在非 app 端,只有 uni-app 编译模式的 nvue 文件才会编译。
![]()
开发 nvue 页面需注意
nvue 页面结构同 vue, 由 template、style、script 构成。
-
template: 模板写法、数据绑定同 vue。组件支持 2 种模式,
- uni-app 组件,同 uni-app 写法。
- App 端 nvue 专用组件,详见uniapp.dcloud.io/component/b…。
-
style:由于采用原生渲染,并非所有浏览器的 css 均支持,布局模型只支持 flex 布局,虽然不会造成某些界面布局无法实现,但写法要注意。详见:样式
-
script:写法同 vue,并支持 3 种 API:
- nvue API :仅支持 App 端,uni-app 编译模式也可使用。使用前需先引入对应模块,参考:模块引入 API
- uni API:uniapp.dcloud.io/api/README
- plus API:仅支持 App 端。www.html5plus.org/doc/h5p.htm…
调试 nvue 页面
HBuilderX 内置了 weex 调试工具的强化版,包括审查界面元素、看 log、debug 打断点,详见
注意事项
-
vue和nvue页面均支持断点调试 - 目前仅支持
nvue页面审查元素,vue页面暂不支持,以及Android平台的nvue审查元素暂不支持查看style - App 端提供真机运行的
console.log日志输出,运行到真机或模拟器时,不用点debug按钮,运行手机 App,会在HBuilderX的控制台直接输出日志。 - 如果是调试
App的界面和常规 API,推荐编译到 H5 端,点HBuilderX右上角的预览,在内置浏览器里调Dom,保存后立即看到结果,调试更方便。并且 H5 端也支持titleNView的各种复杂设置。唯一要注意的就是css兼容性,使用太新的css在pc上预览可能正常,但低端Android上异常,具体可查询caniuse等网站。 - 常用的开发模式就是
pc上使用内置浏览器预览调 dom,运行到真机上看console.log。如果是很复杂的问题才使用debug。 - uni-app 的 App 端的 webkit remote debug,只能调试视图层,不能调试逻辑层。因为 uni-app 的 js 不是运行在 webview 里,而是独立的 jscore 里。
- 部分 manifest 配置,如三方 sdk 配置,需要打包后生效的,可以打包一个自定义运行基座。打包自定义基座后运行这个自定义基座,同样可以真机运行和 debug。打包正式包将无法真机运行和 debug。
- 调试依赖 chrome 安装位置,找不到可能会导致调试报错。社区反馈中有用户主动修改了 chrome 的安装位置,导致内置的 puppeteer 查找 chrome失败,如果你修改了 chrome 的安装位置,请参考 《HBuilderX APP端 uni/nvue 调试报错问题》
nvue开发与vue开发的常见区别
- nvue 页面控制显隐只可以使用
v-if不可以使用v-show - nvue 页面只能使用
flex布局,不支持其他布局方式。页面开发前,首先想清楚这个页面的纵向内容有什么,哪些是要滚动的,然后每个纵向内容的横轴排布有什么,按 flex 布局设计好界面。 - nvue 页面的布局排列方向默认为竖排(
column),如需改变布局方向,可以在manifest.json->app-plus->nvue->flex-direction节点下修改,仅在 uni-app 模式下生效。详情。 - nvue页面编译为H5、小程序时,会做一件css默认值对齐的工作。因为weex渲染引擎只支持flex,并且默认flex方向是垂直。而H5和小程序端,使用web渲染,默认不是flex,并且设置
display:flex后,它的flex方向默认是水平而不是垂直的。所以nvue编译为H5、小程序时,会自动把页面默认布局设为flex、方向为垂直。当然开发者手动设置后会覆盖默认设置。 - 文字内容,必须、只能在
<text>组件下。不能在<div>、<view>的text区域里直接写文字。否则即使渲染了,也无法绑定js里的变量。 - 只有
text标签可以设置字体大小,字体颜色。 - 布局不能使用百分比、没有媒体查询。
- nvue 切换横竖屏时可能导致样式出现问题,建议有 nvue 的页面锁定手机方向。
- 支持的css有限,不过并不影响布局出你需要的界面,
flex还是非常强大的。详见 - 不支持背景图。但可以使用
image组件和层级来实现类似web中的背景效果。因为原生开发本身也没有web这种背景图概念 - css选择器支持的比较少,只能使用 class 选择器。详见
- nvue 的各组件在安卓端默认是透明的,如果不设置
background-color,可能会导致出现重影的问题。 -
class进行绑定时只支持数组语法。 - Android端在一个页面内使用大量圆角边框会造成性能问题,尤其是多个角的样式还不一样的话更耗费性能。应避免这类使用。
- nvue页面没有
bounce回弹效果,只有几个列表组件有bounce效果,包括list、recycle-list、waterfall。 - 原生开发没有页面滚动的概念,页面内容高过屏幕高度并不会自动滚动,只有部分组件可滚动(
list、waterfall、scroll-view/scroller),要滚的内容需要套在可滚动组件下。这不符合前端开发的习惯,所以在 nvue 编译为 uni-app模式时,给页面外层自动套了一个scroller,页面内容过高会自动滚动。(组件不会套,页面有recycle-list时也不会套)。后续会提供配置,可以设置不自动套。 - 在 App.vue 中定义的全局js变量不会在 nvue 页面生效。
globalData和vuex是生效的。 - App.vue 中定义的全局css,对nvue和vue页面同时生效。如果全局css中有些css在nvue下不支持,编译时控制台会报警,建议把这些不支持的css包裹在条件编译里,
APP-PLUS-NVUE - 不能在
style中引入字体文件,nvue 中字体图标的使用参考:加载自定义字体。如果是本地字体,可以用plus.io的API转换路径。 - 目前不支持在 nvue 页面使用
typescript/ts。 21. nvue 页面关闭原生导航栏时,想要模拟状态栏,可以参考文章。但是,仍然强烈建议在nvue页面使用原生导航栏。nvue的渲染速度再快,也没有原生导航栏快。原生排版引擎解析json绘制原生导航栏耗时很少,而解析nvue的js绘制整个页面的耗时要大的多,尤其在新页面进入动画期间,对于复杂页面,没有原生导航栏会在动画期间产生整个屏幕的白屏或闪屏。
iOS 平台下拉组件 refresh 组件注意问题
iOS 平台默认情况下滚动容器组件(如list、waterfall组件)内容不足时,由于没有撑满容器的可视区域会导致无法上下滚动,此时无法操作下拉刷新功能,无法触发refresh组件的@refresh、@pullingdown事件。 此时可在容器组件中配置alwaysScrollableVertical属性值为true来设置支持上下滚动,支持下拉刷新操作。
Android 平台不存在此问题
用法
<list class="scroll-v list" enableBackToTop="true" scroll-y alwaysScrollableVertical="true">
<refresh class="refresh" @refresh="onrefresh()" @pullingdown="onpullingdown">
<!-- refresh content -->
</refresh>
<cell v-for="(newsitem,index) in list" :key="newsitem.id">
<!-- cell content -->
</cell>
</list>
![]()
HTML5+ 能力
uni-app App 端内置 HTML5+ 引擎,让 js 可以直接调用丰富的原生能力
![]()
条件编译调用 HTML5+
小程序及 H5 等平台是没有 HTML5+ 扩展规范的,因此在 uni-app 调用 HTML5+ 的扩展规范时,需要注意使用条件编译。否则运行到h5、小程序等平台会出现 plus is not defined错误。
// #ifdef APP-PLUS
var appid = plus.runtime.appid;
console.log('应用的 appid 为:' + appid);
// #endif
uni-app不需要 plus ready
在html中使用plus的api,需要等待plus ready。 而uni-app不需要等,可以直接使用。而且如果你调用plus ready,反而不会触发。
uni-app 中的事件监听
在普通的 H5+ 项目中,需要使用 document.addEventListener 监听原生扩展的事件。
uni-app 中,没有 document。可以使用 plus.globalEvent.addEventListener 来实现。
// #ifdef APP-PLUS
// 监听新意图事件
plus.globalEvent.addEventListener('newintent', function(){});
// #endif
复制代码
同理,在 uni-app 中使用 Native.js 时,一些 Native.js 中对于原生事件的监听同样需要按照上面的方法去实现。
Native.js 能力
Native.js技术,简称NJS,是一种将手机操作系统的原生对象转义,映射为JS对象,在JS里编写原生代码的技术。 如果说Node.js把js扩展到服务器世界,那么Native.js则把js扩展到手机App的原生世界。 HTML/JS/Css全部语法只有7万多,而原生语法有几十万,Native.js大幅提升了HTML5的能力。 NJS突破了浏览器的功能限制,也不再需要像Hybrid那样由原生语言开发插件才能补足浏览器欠缺的功能。 NJS编写的代码,最终需要在HBuilder里打包发行为App安装包,或者在支持Native.js技术的浏览器里运行。目前Native.js技术不能在普通手机浏览器里直接运行。
- NJS大幅扩展了HTML5的能力范围,原本只有原生或Hybrid App的原生插件才能实现的功能如今可以使用JS实现。
- NJS大幅提升了App开发效率,将iOS、Android、Web的3个工程师组队才能完成的App,变为1个web工程师就搞定。
- NJS不再需要配置原生开发和编译环境,调试、打包均在HBuilder里进行。没有mac和xcode一样可以开发iOS应用。
- 如果不熟悉原生API也没关系,我们汇总了很多NJS的代码示例,复制粘贴就可以用。ask.dcloud.net.cn/article/114
再次强调,Native.js不是一个js库,不需要下载引入到页面的script中,也不像nodejs那样有单独的运行环境,Native.js的运行环境是集成在5+runtime里的,使用HBuilder打包的app或流应用都可以直接使用Native.js。
注意事项:
-
Uni-app不支持Native.js执行UI相关操作的API调用及webview相关API调用。将失效无法正常使用。Uni-app不推荐使用Native.js
-
Native API具有平台依赖性,所以需要通过以下方式判断当前的运行平台
function judgePlatform(){
switch ( plus.os.name ) {
case "Android":
// Android平台: plus.android.*
break;
case "iOS":
// iOS平台: plus.ios.*
break;
default:
// 其它平台
break;
}
}
- 在NJS中调用Native API或从Native API返回数据到NJS时会自动转换数据类型
![]()
技术要求
由于NJS是直接调用Native API,需要对Native API有一定了解,知道所需要的功能调用了哪些原生API,能看懂原生代码并参考原生代码修改为JS代码。 否则只能直接copy别人写好的NJS代码。
renderjs能力
renderjs是一个运行在视图层的js。它比WXS更加强大。它只支持app-vue和web。
renderjs的主要作用有2个:
- 大幅降低逻辑层和视图层的通讯损耗,提供高性能视图交互能力
- 在视图层操作dom,运行 for web 的 js库
![]()
- nvue的视图层是原生的,无法运行js。但提供了bindingx技术来解决通信阻塞。详见
- 微信小程序下替代方案是wxs,这是微信提供的一个裁剪版renderjs。详见
- web下不存在逻辑层和视图层的通信阻塞,也可以直接操作dom,所以在web端使用renderjs主要是为了跨端复用代码。如果只开发web端,没有必要使用renderjs。
<script module="test" lang="renderjs">
export default {
mounted() {
// ...
},
methods: {
// ...
}
}
</script>
过去的问题
- H5端流行的echart报表因为涉及大量dom操作,无法跨端使用
- wx-chart在跨端和更新方面都不足
- 插件市场提供了比wx-chart更好的、全端可用的uChart。但受限于小程序架构逻辑层和视图层分离,导致的通信折损,图表的动画性能不佳。并且uchart只实现了echart的常用功能,还有一些功能没有实现。
新的解决方案
从uni-app 2.5.5+起,新提供了renderjs技术。它是wxs的升级版,一种可以运行在视图层的js。
通过renderjs编写的代码,直接运行在视图层(也就是webview中),可以完整的运行echart等库,并且没有了逻辑层和视图层频繁通行的折损,让动画不再卡顿。
renderjs支持app-vue和h5,不支持其他平台。如果你只考虑这2个平台,可以直接使用本示例,使用完整版的echart。如果还要兼容多端小程序,建议仍然使用uchart。
renderjs,不止能运行echart,其他如F2、threejs等web库都可以运行。
renderjs使用注意事项
- 目前仅支持内联使用。
- 不要直接引用大型类库,推荐通过动态创建 script 方式引用。
- 可以使用 vue 组件的生命周期(不支持 beforeDestroy、destroyed、beforeUnmount、unmounted),不可以使用 App、Page 的生命周期
- 视图层和逻辑层通讯方式与 WXS 一致,另外可以通过 this.$ownerInstance 获取当前组件的 ComponentDescriptor 实例。
- this.$ownerInstance.callMethod() 仅支持调用逻辑层vue选项式中的 methods 中定义的方法。
- 注意逻辑层给数据时最好一次性给到渲染层,而不是不停从逻辑层向渲染层发消息,那样还是会产生逻辑层和视图层的多次通信,还是会卡
- 观测更新的数据在视图层可以直接访问到。
- APP 端视图层的页面引用资源的路径相对于根目录计算,例如:./static/test.js。
- APP 端可以使用 dom、bom API,不可直接访问逻辑层数据,不可以使用 uni 相关接口(如:uni.request)
- H5 端逻辑层和视图层实际运行在同一个环境中,相当于使用 mixin 方式,可以直接访问逻辑层数据。
- vue3 项目不支持
setup script用法。
APP 离线 SDK
App离线开发工具包,即App离线SDK,是把App运行环境(runtime)封装为原生开发调用接口,开发者可以在自己的 Android 及 iOS 原生开发环境配置工程使用,包括 Android离线开发SDK 及 iOS离线开发SDK。
注意:本SDK仅限于uni-app项目使用
- Android 离线SDK-正式版 nativesupport.dcloud.net.cn/AppDocs/dow…
- iOS 离线SDK - 正式版 nativesupport.dcloud.net.cn/AppDocs/dow…
- AppKey申请配置 nativesupport.dcloud.net.cn/AppDocs/use…
使用 Node.js 批量导入多语言标签到 Strapi
在多语言网站开发中,我们常常需要在 Strapi 中维护大量的标签(Tags),比如文章标签、产品分类标签等。如果手动在后台创建上百条标签,会非常耗时且容易出错。本文将介绍如何使用 Node.js 脚本批量导入标签,并支持多语言(英文 / 德语 / 法语)与自动生成 slug。
一、项目背景
假设我们有一个 Next.js + Strapi 项目,Strapi 作为内容管理系统(CMS),我们希望:
- 批量导入 1000+ 标签
- 支持多语言(en / de / fr)
- 自动生成 URL slug
- 避免重复创建
为了实现这些目标,我们可以写一个 Node.js 脚本,调用 Strapi 的 REST API 来批量创建标签。
二、准备工作
-
获取 Strapi API Token 在 Strapi 后台创建一个 API Token,选择 Full Access 或者至少有 Tags CRUD 权限。 在项目根目录创建
.env文件:STRAPI_API_URL=https://your-strapi-domain.com STRAPI_API_TOKEN=YOUR_API_TOKEN -
安装依赖
npm install node-fetch@2 dotenv -
准备标签数据 我们将标签写成
tags.json文件,示例:{ "tags": [ { "title_en": "Economy", "title_de": "Wirtschaft", "title_fr": "Économie", "slug_en": "economy", "slug_de": "wirtschaft", "slug_fr": "economie" }, { "title_en": "Technology", "title_de": "Technologie", "title_fr": "Technologie", "slug_en": "technology", "slug_de": "technologie", "slug_fr": "technologie" } ] }
三、核心脚本解析
以下是 import_tags_to_strapi.js 的核心实现:
const fs = require('fs');
const fetch = require('node-fetch'); // npm install node-fetch@2
require('dotenv').config();
const STRAPI_URL = process.env.STRAPI_API_URL || 'http://localhost:1337';
const TOKEN = process.env.STRAPI_API_TOKEN;
const raw = fs.readFileSync('tags.json', 'utf8');
const { tags } = JSON.parse(raw);
// helper: sleep
const sleep = (ms) => new Promise((res) => setTimeout(res, ms));
1. 创建英文标签
const enBody = {
data: {
title: tagObj.title_en,
slug: tagObj.slug_en
}
};
await fetch(`${STRAPI_URL}/api/tags?populate=none`, {
method: 'POST',
headers: {
Authorization: `Bearer ${TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(enBody)
});
2. 创建德语和法语本地化
const deBody = {
data: { title: tagObj.title_de, slug: tagObj.slug_de },
locale: 'de'
};
await fetch(`${STRAPI_URL}/api/tags/${createdId}/localizations`, { ... });
const frBody = {
data: { title: tagObj.title_fr, slug: tagObj.slug_fr },
locale: 'fr'
};
await fetch(`${STRAPI_URL}/api/tags/${createdId}/localizations`, { ... });
这里使用了 Strapi Localizations API,保证不同语言之间的标签关联。
3. 批量处理与防刷限流
for (let i = 0; i < tags.length; i++) {
await createTag(tags[i], i + 1);
await sleep(200); // 避免 API 请求过快
}
四、运行脚本
node import_tags_to_strapi.js
执行后,你会看到:
1 created EN id= 15
1 created DE localization
1 created FR localization
2 created EN id= 16
...
done import
五、注意事项
- API Token 权限:确保 Token 有 Tag 的读写权限。
-
slug 唯一性:Strapi 对 slug 有唯一性要求,建议提前生成或使用
slugify。 -
请求频率:一次导入大量标签时,增加
sleep时间可避免 Strapi 报 429。 - 多语言管理:Localizations API 保证标签在多语言之间关联,便于前端展示。
六、总结
通过 Node.js 脚本批量导入 Strapi 标签可以大幅提高效率,并且可以:
- 支持上千条标签
- 自动生成 slug
- 支持多语言
- 可在 CI/CD 或部署脚本中重复执行
这种方式特别适合新闻网站、博客、产品目录、跨语言项目等。
【URP】Unity[内置Shader]非光照Unlit
【从UnityURP开始探索游戏渲染】专栏-直达
URP内置Unlit Shader的作用与原理
Unlit Shader是Unity通用渲染管线(URP)中的基础着色器,主要用于渲染不受光照影响的物体。其核心原理是通过直接采样纹理或颜色值输出到屏幕,跳过了复杂的光照计算流程。这种着色器特别适合UI元素、粒子特效、全息投影等需要保持恒定亮度的场景,因为它的渲染结果不会随光照环境变化而改变。
在URP架构中,Unlit Shader通过ShaderLab语法定义,内部使用HLSL编写核心逻辑。与Built-in管线相比,URP版本优化了渲染流程,包含三个关键Pass:主绘制Pass、深度Only Pass和元数据Pass(用于光照烘焙)。其核心特点是:
- 无光照计算:直接输出Albedo颜色或纹理采样结果
- 支持Alpha混合:可实现透明效果
- 移动端优化:减少了GPU指令数量
![]()
发展历史演变
Unlit Shader随着Unity渲染管线的演进经历了三个阶段:
- Built-in管线时期(2012-2018):最初作为简单着色器出现在标准资源包中,使用CG语言编写,功能较为基础
- LWRP过渡期(2018-2020):轻量级渲染管线中首次针对移动平台优化,引入HLSL替代CG
- URP成熟期(2020至今):成为Universal RP的核心组件,支持Shader Graph可视化编程,并优化了多Pass协作机制
具体使用示例
创建Unlit材质的基本步骤:
- 在Project窗口右键创建Material
- 材质Inspector中选择Shader路径:"Universal Render Pipeline/Unlit"
- 配置基础属性:
- Base Map:主纹理贴图
- Base Color:色调叠加
- Alpha:透明度控制
代码说明:
-
定义包含纹理和颜色属性的基础Unlit Shader
-
使用URP核心库中的TransformObjectToHClip方法进行坐标转换
-
片元着色器直接返回纹理采样结果与颜色的乘积
-
UnlitExample.shader
Shader "Custom/UnlitTexture" { Properties { _MainTex ("Texture", 2D) = "white" {} _Color ("Color", Color) = (1,1,1,1) } SubShader { Tags { "RenderType"="Opaque" } Pass { HLSLPROGRAM #pragma vertex vert #pragma fragment frag #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" struct Attributes { float4 positionOS : POSITION; float2 uv : TEXCOORD0; }; struct Varyings { float4 positionCS : SV_POSITION; float2 uv : TEXCOORD0; }; sampler2D _MainTex; float4 _Color; Varyings vert(Attributes IN) { Varyings OUT; OUT.positionCS = TransformObjectToHClip(IN.positionOS.xyz); OUT.uv = IN.uv; return OUT; } half4 frag(Varyings IN) : SV_Target { return tex2D(_MainTex, IN.uv) * _Color; } ENDHLSL } } } -
UnlitGraph.shadergraph
{ "m_Nodes": [ { "m_Id": "d4f5e3c7-1a2d-4b8f-a3e1-6c9b8d2e1f0a", "m_Type": "UnityEditor.ShaderGraph.Texture2DNode", "m_Position": { "x": -208, "y": -16 }, "m_Outputs": [ { "m_Id": "out" } ], "m_Texture": { "m_DefaultValue": {} } }, { "m_Id": "a1b2c3d4-e5f6-7g8h-9i0j-k1l2m3n4o5p6", "m_Type": "UnityEditor.ShaderGraph.ColorNode", "m_Position": { "x": -200, "y": 100 }, "m_Outputs": [ { "m_Id": "out" } ], "m_Color": { "r": 1, "g": 1, "b": 1, "a": 1 } }, { "m_Id": "b2c3d4e5-f6g7-8h9i-0j1k-l2m3n4o5p6q7", "m_Type": "UnityEditor.ShaderGraph.MultiplyNode", "m_Position": { "x": 0, "y": 0 }, "m_Inputs": [ { "m_Id": "a", "m_SlotId": 0 }, { "m_Id": "b", "m_SlotId": 1 } ], "m_Outputs": [ { "m_Id": "out" } ] } ], "m_Edges": [ { "m_OutputSlot": "d4f5e3c7-1a2d-4b8f-a3e1-6c9b8d2e1f0a.out", "m_InputSlot": "b2c3d4e5-f6g7-8h9i-0j1k-l2m3n4o5p6q7.a" }, { "m_OutputSlot": "a1b2c3d4-e5f6-7g8h-9i0j-k1l2m3n4o5p6.out", "m_InputSlot": "b2c3d4e5-f6g7-8h9i-0j1k-l2m3n4o5p6q7.b" } ] }
Shader Graph应用示例
在Shader Graph中创建Unlit效果的步骤:
- 创建新的Shader Graph文件(右键 > Create > Shader > Universal Render Pipeline > Unlit Shader Graph)
- 核心节点配置:
- 添加Sample Texture 2D节点作为基础纹理输入
- 连接Color参数节点实现色调控制
- 使用Multiply节点混合纹理和颜色
- 高级功能扩展:
- 添加Time节点驱动UV动画
- 通过Vertex Position节点实现顶点变形
代码说明:
-
构建包含纹理采样和颜色混合的基础Unlit着色器
-
通过节点连接实现材质属性的可视化编辑
-
可扩展添加UV滚动、顶点动画等高级效果
-
UnlitExample.shader
Shader "Custom/UnlitTexture" { Properties { _MainTex ("Texture", 2D) = "white" {} _Color ("Color", Color) = (1,1,1,1) } SubShader { Tags { "RenderType"="Opaque" } Pass { HLSLPROGRAM #pragma vertex vert #pragma fragment frag #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" struct Attributes { float4 positionOS : POSITION; float2 uv : TEXCOORD0; }; struct Varyings { float4 positionCS : SV_POSITION; float2 uv : TEXCOORD0; }; sampler2D _MainTex; float4 _Color; Varyings vert(Attributes IN) { Varyings OUT; OUT.positionCS = TransformObjectToHClip(IN.positionOS.xyz); OUT.uv = IN.uv; return OUT; } half4 frag(Varyings IN) : SV_Target { return tex2D(_MainTex, IN.uv) * _Color; } ENDHLSL } } } -
UnlitGraph.shadergraph
{ "m_Nodes": [ { "m_Id": "d4f5e3c7-1a2d-4b8f-a3e1-6c9b8d2e1f0a", "m_Type": "UnityEditor.ShaderGraph.Texture2DNode", "m_Position": { "x": -208, "y": -16 }, "m_Outputs": [ { "m_Id": "out" } ], "m_Texture": { "m_DefaultValue": {} } }, { "m_Id": "a1b2c3d4-e5f6-7g8h-9i0j-k1l2m3n4o5p6", "m_Type": "UnityEditor.ShaderGraph.ColorNode", "m_Position": { "x": -200, "y": 100 }, "m_Outputs": [ { "m_Id": "out" } ], "m_Color": { "r": 1, "g": 1, "b": 1, "a": 1 } }, { "m_Id": "b2c3d4e5-f6g7-8h9i-0j1k-l2m3n4o5p6q7", "m_Type": "UnityEditor.ShaderGraph.MultiplyNode", "m_Position": { "x": 0, "y": 0 }, "m_Inputs": [ { "m_Id": "a", "m_SlotId": 0 }, { "m_Id": "b", "m_SlotId": 1 } ], "m_Outputs": [ { "m_Id": "out" } ] } ], "m_Edges": [ { "m_OutputSlot": "d4f5e3c7-1a2d-4b8f-a3e1-6c9b8d2e1f0a.out", "m_InputSlot": "b2c3d4e5-f6g7-8h9i-0j1k-l2m3n4o5p6q7.a" }, { "m_OutputSlot": "a1b2c3d4-e5f6-7g8h-9i0j-k1l2m3n4o5p6.out", "m_InputSlot": "b2c3d4e5-f6g7-8h9i-0j1k-l2m3n4o5p6q7.b" } ] }
实际应用时可结合粒子系统创建发光轨迹,或为UI元素添加动态高亮效果。URP Unlit Shader的轻量级特性使其在移动设备上能保持60fps以上的渲染性能
典型应用场景及实现
光晕效果(Halo)
- 应用实例:角色技能特效、UI高亮提示。通过透明纹理实现边缘发光,如1中描述的透明光晕材质。
- 实现步骤:
- 导入纹理并设置:
Texture Type为Default (sRGB),勾选Alpha Is Transparency,Wrap Mode设为Clamp。 - 创建材质:选择
Universal Render Pipeline/UnlitShader,设置Surface Type为Transparent,拖拽纹理到Base Map插槽。 - 调整
Tint颜色控制光晕色彩。
- 导入纹理并设置:
全息投影效果
- 应用实例:科幻场景中的虚拟角色或界面。结合透明度与扫描线纹理。
- 实现步骤:
- 使用
UnlitShader并启用透明混合(Blend SrcAlpha OneMinusSrcAlpha)。 - 添加顶点偏移代码模拟全息抖动,通过
_Time变量控制动态效果。 - 叠加扫描线纹理(如
_HologramLine1)和菲涅尔反射增强立体感。
- 使用
透明遮罩(如塑料薄膜)
- 应用实例:UI遮罩或半透明装饰物。通过Alpha通道控制透明度,如中的塑料薄膜材质。
- 实现步骤:
- 在图片编辑器中创建带Alpha通道的纹理,白色区域不透明,灰色区域半透明。
- 材质Shader选择
Unlit,设置Transparent模式,纹理绑定到Base Map。
发光广告牌(Billboard)
- 应用实例:游戏内固定亮度标识或霓虹灯。直接显示纹理颜色不受光照影响。
- 实现步骤:
- 使用
UnlitShader,Surface Type设为Opaque。 - 通过
Base Map设置发光纹理,调整Tint颜色增强亮度。
- 使用
景深遮挡标记
- 应用实例:半透明物体深度写入(如玻璃瓶),解决景深效果失效问题。
- 实现步骤:
- 创建两个材质:一个透明材质(
Queue=Transparent),一个深度写入材质(Queue=2000)。 - 深度写入材质使用
UnlitShader并启用ZWrite On。
- 创建两个材质:一个透明材质(
关键注意事项
- 渲染顺序:透明物体需关闭深度写入(
ZWrite Off),并合理设置Queue标签避免混合错误。 - 性能优化:复杂效果(如全息投影)建议结合顶点着色器计算,减少片元着色器负担
【从UnityURP开始探索游戏渲染】专栏-直达 (欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)