-
很显然无法直接 pip install,自己找源码编译,需要依赖 OpenGL 而 Ascend 计算卡又不支持。如果安装 mesa 又报错各种依赖装不上。论坛上有朋友成功搞定过的嘛?
-
【功能模块】3D渲染库kaolin【操作步骤&问题现象】1、使用mindspore框架做三维重建的过程,需要使用到kaolin.render.mesh.dibr_rasterization()算子2、源码中包括kaolin._C.render.mesh.rasterize_backward_cuda如何实现该算子呢【截图信息】
-
浏览器渲染机制浏览器采用流式布局模型(Flow Based Layout)浏览器会把HTML解析成DOM,把CSS解析成CSSOM,DOM和CSSOM合并就产生了渲染树(Render Tree)。有了RenderTree,我们就知道了所有节点的样式,然后计算他们在页面上的大小和位置,最后把节点绘制到页面上。由于浏览器使用流式布局,对Render Tree的计算通常只需要遍历一次就可以完成,但table及其内部元素除外,他们可能需要多次计算,通常要花3倍于同等元素的时间,这也是为什么要避免使用table布局的原因之一。2. 重绘由于节点的几何属性发生改变或者由于样式发生改变而不会影响布局的,称为重绘,例如outline, visibility, color、background-color等,重绘的代价是高昂的,因为浏览器必须验证DOM树上其他节点元素的可见性。3. 回流回流是布局或者几何属性需要改变就称为回流。回流是影响浏览器性能的关键因素,因为其变化涉及到部分页面(或是整个页面)的布局更新。一个元素的回流可能会导致了其所有子元素以及DOM中紧随其后的节点、祖先节点元素的随后的回流。<body> <div class="error"> <h4>我的组件</h4> <p><strong>错误:</strong>错误的描述…</p> <h5>错误纠正</h5> <ol> <li>第一步</li> <li>第二步</li> </ol> </div> </body>在上面的HTML片段中,对该段落(标签)回流将会引发强烈的回流,因为它是一个子节点。这也导致了祖先的回流(div.error和body – 视浏览器而定)。此外,和也会有简单的回流,因为其在DOM中在回流元素之后。大部分的回流将导致页面的重新渲染。回流必定会发生重绘,重绘不一定会引发回流。4. 浏览器优化现代浏览器大多都是通过队列机制来批量更新布局,浏览器会把修改操作放在队列中,至少一个浏览器刷新(即16.6ms)才会清空队列,但当你获取布局信息的时候,队列中可能有会影响这些属性或方法返回值的操作,即使没有,浏览器也会强制清空队列,触发回流与重绘来确保返回正确的值。主要包括以下属性或方法:offsetTop、offsetLeft、offsetWidth、offsetHeightscrollTop、scrollLeft、scrollWidth、scrollHeightclientTop、clientLeft、clientWidth、clientHeightwidth、heightgetComputedStyle()getBoundingClientRect()所以,我们应该避免频繁的使用上述的属性,他们都会强制渲染刷新队列。5. 减少重绘与回流CSS使用 transform 替代 top使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局避免使用table布局,可能很小的一个小改动会造成整个 table 的重新布局。尽可能在DOM树的最末端改变class,回流是不可避免的,但可以减少其影响。尽可能在DOM树的最末端改变class,可以限制了回流的范围,使其影响尽可能少的节点。避免设置多层内联样式,CSS 选择符从右往左匹配查找,避免节点层级过多。<div> <a> <span></span> </a> </div> <style> span { color: red; } div > a > span { color: red; } </style>对于第一种设置样式的方式来说,浏览器只需要找到页面中所有的 span 标签然后设置颜色,但是对于第二种设置样式的方式来说,浏览器首先需要找到所有的 span 标签,然后找到 span 标签上的 a 标签,最后再去找到 div 标签,然后给符合这种条件的 span 标签设置颜色,这样的递归过程就很复杂。所以我们应该尽可能的避免写过于具体的 CSS 选择器,然后对于 HTML 来说也尽量少的添加无意义标签,保证层级扁平。将动画效果应用到position属性为absolute或fixed的元素上,避免影响其他元素的布局,这样只是一个重绘,而不是回流,同时,控制动画速度可以选择 requestAnimationFrame,详见探讨 requestAnimationFrame。避免使用CSS表达式,可能会引发回流。将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点,例如will-change、video、iframe等标签,浏览器会自动将该节点变为图层。CSS3 硬件加速(GPU加速),使用css3硬件加速,可以让transform、opacity、filters这些动画不会引起回流重绘 。但是对于动画的其它属性,比如background-color这些,还是会引起回流重绘的,不过它还是可以提升这些动画的性能。JavaScript避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。
-
浏览器渲染机制浏览器采用流式布局模型(Flow Based Layout)浏览器会把HTML解析成DOM,把CSS解析成CSSOM,DOM和CSSOM合并就产生了渲染树(Render Tree)。有了RenderTree,我们就知道了所有节点的样式,然后计算他们在页面上的大小和位置,最后把节点绘制到页面上。由于浏览器使用流式布局,对Render Tree的计算通常只需要遍历一次就可以完成,但table及其内部元素除外,他们可能需要多次计算,通常要花3倍于同等元素的时间,这也是为什么要避免使用table布局的原因之一。2. 重绘由于节点的几何属性发生改变或者由于样式发生改变而不会影响布局的,称为重绘,例如outline, visibility, color、background-color等,重绘的代价是高昂的,因为浏览器必须验证DOM树上其他节点元素的可见性。3. 回流回流是布局或者几何属性需要改变就称为回流。回流是影响浏览器性能的关键因素,因为其变化涉及到部分页面(或是整个页面)的布局更新。一个元素的回流可能会导致了其所有子元素以及DOM中紧随其后的节点、祖先节点元素的随后的回流。<body> <div class="error"> <h4>我的组件</h4> <p><strong>错误:</strong>错误的描述…</p> <h5>错误纠正</h5> <ol> <li>第一步</li> <li>第二步</li> </ol> </div> </body>在上面的HTML片段中,对该段落(标签)回流将会引发强烈的回流,因为它是一个子节点。这也导致了祖先的回流(div.error和body – 视浏览器而定)。此外,和也会有简单的回流,因为其在DOM中在回流元素之后。大部分的回流将导致页面的重新渲染。回流必定会发生重绘,重绘不一定会引发回流。4. 浏览器优化现代浏览器大多都是通过队列机制来批量更新布局,浏览器会把修改操作放在队列中,至少一个浏览器刷新(即16.6ms)才会清空队列,但当你获取布局信息的时候,队列中可能有会影响这些属性或方法返回值的操作,即使没有,浏览器也会强制清空队列,触发回流与重绘来确保返回正确的值。主要包括以下属性或方法:offsetTop、offsetLeft、offsetWidth、offsetHeightscrollTop、scrollLeft、scrollWidth、scrollHeightclientTop、clientLeft、clientWidth、clientHeightwidth、heightgetComputedStyle()getBoundingClientRect()所以,我们应该避免频繁的使用上述的属性,他们都会强制渲染刷新队列。5. 减少重绘与回流CSS使用 transform 替代 top使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局避免使用table布局,可能很小的一个小改动会造成整个 table 的重新布局。尽可能在DOM树的最末端改变class,回流是不可避免的,但可以减少其影响。尽可能在DOM树的最末端改变class,可以限制了回流的范围,使其影响尽可能少的节点。避免设置多层内联样式,CSS 选择符从右往左匹配查找,避免节点层级过多。<div> <a> <span></span> </a> </div> <style> span { color: red; } div > a > span { color: red; } </style>对于第一种设置样式的方式来说,浏览器只需要找到页面中所有的 span 标签然后设置颜色,但是对于第二种设置样式的方式来说,浏览器首先需要找到所有的 span 标签,然后找到 span 标签上的 a 标签,最后再去找到 div 标签,然后给符合这种条件的 span 标签设置颜色,这样的递归过程就很复杂。所以我们应该尽可能的避免写过于具体的 CSS 选择器,然后对于 HTML 来说也尽量少的添加无意义标签,保证层级扁平。将动画效果应用到position属性为absolute或fixed的元素上,避免影响其他元素的布局,这样只是一个重绘,而不是回流,同时,控制动画速度可以选择 requestAnimationFrame,详见探讨 requestAnimationFrame。避免使用CSS表达式,可能会引发回流。将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点,例如will-change、video、iframe等标签,浏览器会自动将该节点变为图层。CSS3 硬件加速(GPU加速),使用css3硬件加速,可以让transform、opacity、filters这些动画不会引起回流重绘 。但是对于动画的其它属性,比如background-color这些,还是会引起回流重绘的,不过它还是可以提升这些动画的性能。JavaScript避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。
-
前言这些内容是博主在学习过程中记录下来的,有一些不重要的点就跳过了,需要时自行查询文档。其实V2到V3的学习成本不高,熟悉V2的话,看完这篇文章就可以上手V3。Vue3官网在线源码编译地址1,setupsetup是所有Composition API的容器,值为一个函数。组件中所用到的数据、方法等等,均要配置在setup中,它会在beforeCreate之前执行一次,注意:V3里this不再是指向Vue实例,访问this会是undefined1.1,返回值若返回一个对象,则对象中的属性、方法, 在模板中均可以直接使用。若返回一个渲染函数:则可以自定义渲染内容。1.2,注意点尽量不要与V2配置混用V2配置(data、methos、computed…)中可以访问到setup中的属性、方法。但在setup中不能访问到V2配置(data、methods、computed…)。如果有重名, setup优先。setup不能是一个async函数因为返回值不再return的对象, 而是promise, 模板看不到return对象中的属性。(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)1.3,语法<script>import { ref, reactive } from 'vue'export default { name: 'Home', setup(props, context) { const title = ref('标题') const data = reactive({ value: '哈哈哈' }) return { title, data } }}</script>1.4,setup的参数props:值为对象,包含组件外部传递过来,且组件内部声明接收了的属性context:上下文对象attrs: 值为对象,包含组件外部传递过来,但没有在props配置中声明的属性, 相当于this.$attrsslots: 收到的插槽内容, 相当于this.$slotsemit: 分发自定义事件的函数, 相当于this.$emit2,ref 创建响应式数据使用ref可以创建一个包含响应式数据的引用对象(reference对象,简称ref对象),可以是基本类型、也可以是对象。语法// 创建const xxx = ref(value)// 使用xxx.value// 在模板中<div>{{xxx}}</div>3,reactive 创建响应式数据定义一个对象类型的响应式数据,内部基于ES6的Proxy实现,通过代理对象操作源对象内部数据进行操作语法// 创建const xxx = reactive({ xxx: ''})// 使用xxx.xxx4,computed 计算属性与V2中computed配置功能一致语法import { computed } from 'vue'setup(){ // 简写语法 let fullName = computed(() => { return person.firstName + '-' + person.lastName }) // 完整语法 let fullName = computed({ get(){ return person.firstName + '-' + person.lastName }, set(value){ const nameArr = value.split('-') person.firstName = nameArr[0] person.lastName = nameArr[1] } }) return fullName5,watch 监听与V2中watch配置功能一致,语法有点改动语法情况一:监视ref定义的响应式数据watch(sum, (newValue, oldValue) => { console.log('sum变化了', newValue, oldValue)}, {immediate:true})情况二:监视多个ref定义的响应式数据watch([sum, msg], (newValue,oldValue) => { console.log('sum或msg变化了', newValue,oldValue)}) 情况三:监视reactive定义的响应式数据// 若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue// 若watch监视的是reactive定义的响应式数据,则强制开启了深度监视watch(person, (newValue, oldValue) => { console.log('person变化了', newValue, oldValue)}, { immediate:true, deep:false }) // 此处的deep配置不再奏效情况四:监视reactive定义的响应式数据中的某个属性watch(() => person.job, (newValue, oldValue) => { console.log('person的job变化了', newValue, oldValue)}, { immediate:true, deep:true }) 情况五:监视reactive定义的响应式数据中的某些属性watch([() => person.job, () => person.name], (newValue, oldValue) => { console.log('person的job变化了', newValue, oldValue)}, { immediate:true, deep:true })特殊情况:此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效watch(() => person.job, (newValue, oldValue) => { console.log('person的job变化了', newValue, oldValue)}, { deep:true })6,watchEffect 监听回调和watch的区别是,watch既要指明监视的属性,也要指明监视的回调。而watchEffect,不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性,不用写返回值。语法// 回调中用到的数据只要发生变化,则直接重新执行回调watchEffect(() => { const x1 = sum.value const x2 = person.age console.log('watchEffect配置的回调执行了')})7,生命周期生命周期全都写在setup中7.1,改变beforeDestroy 改名为 beforeUnmountdestroyed 改名为 unmountedbeforeCreate => setupcreated => setupbeforeMount => onBeforeMountmounted => onMountedbeforeUpdate => onBeforeUpdateupdated => onUpdatedbeforeUnmount => onBeforeUnmountunmounted => onUnmounted7.2,语法setup() { onMounted(() => { console.log('mounted') })}8,toRef 创建ref创建一个ref对象,其value值指向另一个对象中的某个属性语法const state = reactive({ foo: 1, bar: 2})const fooRef = toRef(state, 'foo')// 传递propsexport default { setup(props) { useSomeFeature(toRef(props, 'foo')) }}9,toRefs 响应式转换将响应式对象转换为普通对象,其中结果对象的每个property都是指向原始对象相应property的ref语法const state = reactive({ foo: 1, bar: 2})const stateAsRefs = toRefs(state)// 此时state和stateAsRefs是关联的10,shallowReactive 响应式外层转换只处理对象最外层属性的响应式(浅响应式)。适用于:一个对象数据,结构比较深, 但变化时只是外层属性变化语法const state = shallowReactive({ foo: 1, nested: { bar: 2 }})11,shallowRef 基本数据响应式只处理基本数据类型的响应式, 不进行对象的响应式处理。适用于:一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换语法const shallow = shallowRef({ greet: 'Hello, world'})12,readonly 响应式变只读让一个响应式数据变为只读的(深只读),应用于不希望数据被修改时语法const shallow = shallowRef({ greet: 'Hello, world', // 只读 nested: { bar: 2 // 只读 }})13,shallowReadonly 响应式变只读让一个响应式数据变为只读的(浅只读),应用于不希望数据被修改时语法const shallow = shallowReadonly({ foo: 1, // 只读 nested: { bar: 2 // 非只读 }})14,toRaw 响应式变非响应式将一个由reactive生成的响应式对象转为普通对象,对这个普通对象的所有操作,不会引起页面更新。语法const foo = {}const Foo = reactive(foo)console.log(toRaw(Foo) === foo) // true15,markRaw 标记永远不响应式标记一个对象,使其永远不会再成为响应式对象,有些值不应被设置为响应式的,例如复杂的第三方类库等,当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。语法const foo = markRaw({})console.log(isReactive(reactive(foo))) // false// 嵌套在其他响应式对象中时也可以使用const bar = reactive({ foo })console.log(isReactive(bar.foo)) // false16,customRef 依赖更新控制创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。它需要一个工厂函数,该函数接收track和trigger函数作为参数,并且应该返回一个带有get和set的对象。语法<script>import { customRef } from 'vue'export default { name: 'Home', setup() { // 实现防抖函数 const fn = function(value, delay = 500) { let timeout return customRef((track, trigger) => { return { get() { track() return value }, set(newValue) { clearInterval(timeout) timeout = setTimeout(() => { console.log(newValue) value = newValue trigger() }, delay) } } }) } const keyword = fn('', 500) return { keyword } }}</script>17,provide & inject 通信实现祖与后代组件间通信,父组件有一个provide选项来提供数据,后代组件有一个inject选项来开始使用这些数据语法// 祖组件setup(){ let car = reactive({ name:'奔驰', price:'40万' }) provide('car', car)}// 后代组件setup(props, context){ const car = inject('car') return { car }}18,响应式数据的判断18.1,isRef检查一个值是否为一个ref对象语法const val = ref('xxx')isRef(val) // true18.2,isReactive检查一个值是否为一个isReactive对象语法const val = isReactive({})isRef(val) // true18.3,isReadonly检查一个对象是否是由readonly创建的只读代理语法const state = reactive({ name: 'John'})console.log(isReactive(state)) // true18.4,isProxy检查对象是否是由reactive或readonly创建的proxy语法const state = reactive({ name: 'John'})console.log(isProxy(state)) // true19,teleport 移动dom组件Teleport提供了一种干净的方法,允许我们控制在DOM中哪个父节点下渲染了HTML,而不必求助于全局状态或将其拆分为两个组件。语法<teleport to="移动位置"> <div v-if="isShow" class="mask"> <div class="dialog"> <h3>我是一个弹窗</h3> <button @click="isShow = false">关闭弹窗</button> </div> </div></teleport>// to的格式<teleport to="#some-id" /><teleport to=".some-class" /><teleport to="[data-teleport]" />// disabled的格式<teleport to="#popup" :disabled="displayVideoInline"> <video src="./my-movie.mp4"></teleport>20,Suspense 异步渲染组件等待异步组件时先渲染一些额外内容,让应用有更好的用户体验语法<template> <div class="app"> <h3>我是App组件</h3> <Suspense> <template #default> <Child/> </template> <template #fallback> <h3>加载中.....</h3> </template> </Suspense> </div></template>import { defineAsyncComponent } from 'vue'const Child = defineAsyncComponent(() => import('./components/Child.vue'))components: { Child}21,全局API调整将全局的API,即:Vue.xxx调整到应用实例(app)上V2的api V3的apiVue.config.xxxx app.config.xxxxVue.component app.componentVue.directive app.directiveVue.mixin app.mixinVue.use app.useVue.prototype app.config.globalProperties22,移除api名称 现状Vue.config.productionTip 已移除config.keyCodes 已移除$children 已移除$listeners 已移除$on 已移除$off 已移除$once 已移除filters 已移除.native 已移除23,Ref 获取DOM由于V3中不在存在this,所以ref的获取调整了23.1,单个ref语法<div ref="Qrcode" class="qr_codeode_url" />import { ref } from 'vue'export default { setup() { const Qrcode = ref(null) // 挂载后 onMounted(() => { console.log(Qrcode.value) }) return { Qrcode } }}23.2,循环中的refV3中在for循环元素上绑定ref将不再自动创建$ref数组。要从单个绑定获取多个ref,请将ref绑定到一个更灵活的函数上语法<div v-for="item in list" :ref="setItemRef"></div>import { onBeforeUpdate, onUpdated } from 'vue'export default { setup() { let itemRefs = [] const setItemRef = el => { if (el) { itemRefs.push(el) } } onBeforeUpdate(() => { itemRefs = [] }) onUpdated(() => { console.log(itemRefs) }) return { setItemRef } }}itemRefs不必是数组:它也可以是一个对象,其ref可以通过迭代的key被设置如有需要,itemRef也可以是响应式的,且可以被侦听24,emits 自定义事件定义一个组件可以向其父组件触发的事件// 在子组件中<h1 @click="father">{{ msg }}</h1>export default { name: 'HelloWorld', props: { msg: { type: String, default: '' } }, emits: ['close'], setup(props, { emit }) { const father = function() { emit('close', 'child') } return { father } }}// 在父组件中<HelloWorld :msg="msg" @click="fn" @close="fn2" />25,$nextTick 异步更新使用方式修改import { nextTick } from 'vue'nextTick(() => { // ...})26,hook 生命周期事件通过事件来监听组件生命周期中的关键阶段语法// V2的语法<template> <child-component @hook:updated="onUpdated"></template>// V3的语法<template> <child-component @vnode-updated="onUpdated"></template>// 驼峰写法<template> <child-component @vnodeUpdated="onUpdated"></template————————————————版权声明:本文为CSDN博主「鹏多多i」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/pdd11997110103/article/details/121035412
-
浏览器渲染机制浏览器采用流式布局模型(Flow Based Layout)浏览器会把HTML解析成DOM,把CSS解析成CSSOM,DOM和CSSOM合并就产生了渲染树(Render Tree)。有了RenderTree,我们就知道了所有节点的样式,然后计算他们在页面上的大小和位置,最后把节点绘制到页面上。由于浏览器使用流式布局,对Render Tree的计算通常只需要遍历一次就可以完成,但table及其内部元素除外,他们可能需要多次计算,通常要花3倍于同等元素的时间,这也是为什么要避免使用table布局的原因之一。2. 重绘由于节点的几何属性发生改变或者由于样式发生改变而不会影响布局的,称为重绘,例如outline, visibility, color、background-color等,重绘的代价是高昂的,因为浏览器必须验证DOM树上其他节点元素的可见性。3. 回流回流是布局或者几何属性需要改变就称为回流。回流是影响浏览器性能的关键因素,因为其变化涉及到部分页面(或是整个页面)的布局更新。一个元素的回流可能会导致了其所有子元素以及DOM中紧随其后的节点、祖先节点元素的随后的回流。<body> <div class="error"> <h4>我的组件</h4> <p><strong>错误:</strong>错误的描述…</p> <h5>错误纠正</h5> <ol> <li>第一步</li> <li>第二步</li> </ol> </div> </body>在上面的HTML片段中,对该段落(标签)回流将会引发强烈的回流,因为它是一个子节点。这也导致了祖先的回流(div.error和body – 视浏览器而定)。此外,和也会有简单的回流,因为其在DOM中在回流元素之后。大部分的回流将导致页面的重新渲染。回流必定会发生重绘,重绘不一定会引发回流。4. 浏览器优化现代浏览器大多都是通过队列机制来批量更新布局,浏览器会把修改操作放在队列中,至少一个浏览器刷新(即16.6ms)才会清空队列,但当你获取布局信息的时候,队列中可能有会影响这些属性或方法返回值的操作,即使没有,浏览器也会强制清空队列,触发回流与重绘来确保返回正确的值。主要包括以下属性或方法:offsetTop、offsetLeft、offsetWidth、offsetHeightscrollTop、scrollLeft、scrollWidth、scrollHeightclientTop、clientLeft、clientWidth、clientHeightwidth、heightgetComputedStyle()getBoundingClientRect()所以,我们应该避免频繁的使用上述的属性,他们都会强制渲染刷新队列。5. 减少重绘与回流CSS使用 transform 替代 top使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局避免使用table布局,可能很小的一个小改动会造成整个 table 的重新布局。尽可能在DOM树的最末端改变class,回流是不可避免的,但可以减少其影响。尽可能在DOM树的最末端改变class,可以限制了回流的范围,使其影响尽可能少的节点。避免设置多层内联样式,CSS 选择符从右往左匹配查找,避免节点层级过多。<div> <a> <span></span> </a> </div> <style> span { color: red; } div > a > span { color: red; } </style>对于第一种设置样式的方式来说,浏览器只需要找到页面中所有的 span 标签然后设置颜色,但是对于第二种设置样式的方式来说,浏览器首先需要找到所有的 span 标签,然后找到 span 标签上的 a 标签,最后再去找到 div 标签,然后给符合这种条件的 span 标签设置颜色,这样的递归过程就很复杂。所以我们应该尽可能的避免写过于具体的 CSS 选择器,然后对于 HTML 来说也尽量少的添加无意义标签,保证层级扁平。将动画效果应用到position属性为absolute或fixed的元素上,避免影响其他元素的布局,这样只是一个重绘,而不是回流,同时,控制动画速度可以选择 requestAnimationFrame,详见探讨 requestAnimationFrame。避免使用CSS表达式,可能会引发回流。将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点,例如will-change、video、iframe等标签,浏览器会自动将该节点变为图层。CSS3 硬件加速(GPU加速),使用css3硬件加速,可以让transform、opacity、filters这些动画不会引起回流重绘 。但是对于动画的其它属性,比如background-color这些,还是会引起回流重绘的,不过它还是可以提升这些动画的性能。JavaScript避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。
-
我现在想把一些用pytorch3d写的可微渲染部分的代码转换为mindspore,但我查了一下文档,好像没有看到相关的内容。有没有什么可行的方法可以让我把pytorch3d的代码重写为mindspore框架下呢?
-
【功能模块】kunpeng920服务器通过AMDGPU渲染【操作步骤&问题现象】1、在鲲鹏920,上带AMD wx5100 GPU,android asop 调用 mesa-22.0.0 and mesa-21.1.6进行渲染。都出现异常。2、【截图信息】【日志信息】(可选,上传日志内容或者附件)03-18 11:55:52.415 14435 14435 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 03-18 11:55:52.415 14435 14435 F DEBUG : Build fingerprint: 'redroid/redroid_arm64_only/redroid_arm64_only:12/SQ1D.220205.004/eng.frank.20220305.064003:userdebug/test-keys' 03-18 11:55:52.415 14435 14435 F DEBUG : Revision: '0' 03-18 11:55:52.415 14435 14435 F DEBUG : ABI: 'arm64' 03-18 11:55:52.415 14435 14435 F DEBUG : Timestamp: 2022-03-18 11:55:52.156479216+0000 03-18 11:55:52.415 14435 14435 F DEBUG : Process uptime: 0s 03-18 11:55:52.415 14435 14435 F DEBUG : Cmdline: com.android.systemui 03-18 11:55:52.415 14435 14435 F DEBUG : pid: 14118, tid: 14429, name: GrallocUploadTh >>> com.android.systemui <<< 03-18 11:55:52.415 14435 14435 F DEBUG : uid: 10062 03-18 11:55:52.415 14435 14435 F DEBUG : signal 7 (SIGBUS), code 2 (BUS_ADRERR), fault addr 0xefc1a6277000 03-18 11:55:52.415 14435 14435 F DEBUG : x0 0000efc1a6277000 x1 b400efc0af1ec250 x2 00000000000000e8 x3 0000efc1a6277000 03-18 11:55:52.415 14435 14435 F DEBUG : x4 b400efc0af1ec338 x5 0000efc1a62770e8 x6 0000000000000000 x7 0000000000000000 03-18 11:55:52.415 14435 14435 F DEBUG : x8 0000000000000001 x9 0000000000000000 x10 0000000000000004 x11 000000000000003e 03-18 11:55:52.415 14435 14435 F DEBUG : x12 0000000000000000 x13 0000000000000000 x14 b400efc0af1ec250 x15 0000000000000000 03-18 11:55:52.415 14435 14435 F DEBUG : x16 0000efbe6c198580 x17 0000efc190dd9c20 x18 0000efbe1d5d0000 x19 0000000000000100 03-18 11:55:52.415 14435 14435 F DEBUG : x20 0000efc1a6277000 x21 b400efc0af1ec250 x22 00000000000000e8 x23 000000000000003e 03-18 11:55:52.415 14435 14435 F DEBUG : x24 0000000000000001 x25 00000000000000e8 x26 00000000000000e8 x27 0000efc1a6277000 03-18 11:55:52.415 14435 14435 F DEBUG : x28 000000000000003a x29 0000efbe1e343420 03-18 11:55:52.415 14435 14435 F DEBUG : lr 0000efbe6b77d200 sp 0000efbe1e343410 pc 0000efc190dd9ba8 pst 0000000020000000 03-18 11:55:52.415 14435 14435 F DEBUG : backtrace: 03-18 11:55:52.415 14435 14435 F DEBUG : #00 pc 000000000004bba8 /apex/com.android.runtime/lib64/bionic/libc.so (__memcpy+248) (BuildId: ba489d4985c0cf173209da67405662f9) 03-18 11:55:52.415 14435 14435 F DEBUG : #01 pc 00000000004c51fc /vendor/lib64/dri/libgallium_dri.so (BuildId: 836ba0e12074242913f46921674e5afb) 03-18 11:55:52.415 14435 14435 F DEBUG : #02 pc 00000000009d7dc4 /vendor/lib64/dri/libgallium_dri.so (BuildId: 836ba0e12074242913f46921674e5afb) 03-18 11:55:52.415 14435 14435 F DEBUG : #03 pc 0000000000a8bfc8 /vendor/lib64/dri/libgallium_dri.so (BuildId: 836ba0e12074242913f46921674e5afb) 03-18 11:55:52.415 14435 14435 F DEBUG : #04 pc 0000000000716e94 /vendor/lib64/dri/libgallium_dri.so (BuildId: 836ba0e12074242913f46921674e5afb) 03-18 11:55:52.415 14435 14435 F DEBUG : #05 pc 0000000000698a9c /vendor/lib64/dri/libgallium_dri.so (BuildId: 836ba0e12074242913f46921674e5afb) 03-18 11:55:52.415 14435 14435 F DEBUG : #06 pc 0000000000697f80 /vendor/lib64/dri/libgallium_dri.so (BuildId: 836ba0e12074242913f46921674e5afb) 03-18 11:55:52.415 14435 14435 F DEBUG : #07 pc 0000000000698138 /vendor/lib64/dri/libgallium_dri.so (BuildId: 836ba0e12074242913f46921674e5afb) 03-18 11:55:52.415 14435 14435 F DEBUG : #08 pc 0000000000505238 /system/lib64/libhwui.so (std::__1::__packaged_task_func<android::uirenderer::EGLUploader::onUploadHardwareBitmap(SkBitmap const&, android::uirenderer::FormatInfo const&, AHardwareBuffer*)::'lambda'(), std::__1::allocator<android::uirenderer::EGLUploader::onUploadHardwareBitmap(SkBitmap const&, android::uirenderer::FormatInfo const&, AHardwareBuffer*)::'lambda'()>, void* ()>::operator()()+120) (BuildId: 3d3592fb15b099f3bc3663e5c01ad8af) 03-18 11:55:52.415 14435 14435 F DEBUG : #09 pc 000000000020ef68 /system/lib64/libhwui.so (std::__1::__function::__func<decltype(fp()) android::uirenderer::WorkQueue::runSync<android::uirenderer::EGLUploader::onUploadHardwareBitmap(SkBitmap const&, android::uirenderer::FormatInfo const&, AHardwareBuffer*)::'lambda'()>(android::uirenderer::EGLUploader::onUploadHardwareBitmap(SkBitmap const&, android::uirenderer::FormatInfo const&, AHardwareBuffer*)::'lambda'()&&)::'lambda'(), std::__1::allocator<decltype(fp()) android::uirenderer::WorkQueue::runSync<android::uirenderer::EGLUploader::onUploadHardwareBitmap(SkBitmap const&, android::uirenderer::FormatInfo const&, AHardwareBuffer*)::'lambda'()>(android::uirenderer::EGLUploader::onUploadHardwareBitmap(SkBitmap const&, android::uirenderer::FormatInfo const&, AHardwareBuffer*)::'lambda'()&&)::'lambda'()>, void ()>::operator()()+88) (BuildId: 3d3592fb15b099f3bc3663e5c01ad8af) 03-18 11:55:52.415 14435 14435 F DEBUG : #10 pc 00000000003c62c8 /system/lib64/libhwui.so (android::uirenderer::WorkQueue::process()+156) (BuildId: 3d3592fb15b099f3bc3663e5c01ad8af) 03-18 11:55:52.415 14435 14435 F DEBUG : #11 pc 00000000004ebc7c /system/lib64/libhwui.so (android::uirenderer::ThreadBase::threadLoop()+72) (BuildId: 3d3592fb15b099f3bc3663e5c01ad8af) 03-18 11:55:52.415 14435 14435 F DEBUG : #12 pc 00000000000120ac /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+260) (BuildId: d5dfcc2a0782d6e050d6cf3448f6af45) 03-18 11:55:52.415 14435 14435 F DEBUG : #13 pc 0000000000011964 /system/lib64/libutils.so (thread_data_t::trampoline(thread_data_t const*)+404) (BuildId: d5dfcc2a0782d6e050d6cf3448f6af45) 03-18 11:55:52.415 14435 14435 F DEBUG : #14 pc 00000000000b1910 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+264) (BuildId: ba489d4985c0cf173209da67405662f9) 03-18 11:55:52.415 14435 14435 F DEBUG : #15 pc 00000000000513f0 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: ba489d4985c0cf173209da67405662f9)
-
Cuttlefish 的加速图形模式会使用主机的物理图形处理器 (GPU) 进行渲染,具体方法是将客户机渲染命令传递给主机,在主机上运行渲染命令调用,然后将渲染后的结果传递回客户机。默认情况下,Cuttlefish 设备中的客户机端渲染(例如界面和视频播放)由 SwiftShader 处理。SwiftShader 是对 OpenGL API 和 Vulkan API 的软件实现。由于 SwiftShader 是一种软件实现,因此它为 Cuttlefish 提供了一种可在任何主机上运行的通用渲染解决方案。不过,使用 SwiftShader 的性能不及使用正常设备。渲染是一种可大规模并行处理的并行问题,因为像素值是可以单独计算的。图形处理器 (GPU) 是通过加速渲染解决此问题的硬件单元。要求加速图形模式要求主机具有以下驱动程序:支持 EGL 的驱动程序(支持 GL_KHR_surfaceless_context 扩展程序)支持 OpenGL ES 的驱动程序支持 Vulkan 的驱动程序使用加速图形模式GfxStream若要使用 GfxStream 加速图形模式,请使用 --gpu_mode=gfxstream 标记启动本地 Cuttlefish 设备。使用此模式时,OpenGL 和 Vulkan API 调用会直接转到主机。$launch_cvd --gpu_mode=gfxstreamVirgl若要使用 Virgl 加速图形模式,请使用 --gpu_mode=drm_virgl 标记启动本地 Cuttlefish 设备。$launch_cvd --gpu_mode=drm_virgl使用 Virgl 加速图形模式时,OpenGL API 调用会转换为中间表示形式(请参阅 Gallium3D)。系统会将相应中间表示形式传递给主机,并且主机上的 virglrenderer 库会将此中间表示形式重新转换为 OpenGL API 调用。注意:此模式不支持 Vulkan。 声明: 本文转自:https://source.android.google.cn/setup/create/cuttlefish-ref-gpu,仅供学习与交流,非商业用途,版权归原作者所有,如有侵权,请联系删除。
-
【功能模块】【操作步骤&问题现象】1、2、【截图信息】【日志信息】(可选,上传日志内容或者附件)
-
一.Jinja2语法基本语法看过之前模板的例子就知道,Jinja2构成的模板文件中,文本内容大致可以分三种。1 变量取值或者宏调用 {{ }}2 控制结构 {% %}3 注释 {# #} 1 jinja2变量jinja2模板中使用 {{ }} 语法表示一个变量,它是一种特殊的占位符。当利用jinja2进行渲染的时候,它会把这些特殊的占位符进行填充/替换,jinja2支持python中所有的Python数据类型比如列表、字段、对象等。2 jinja2结构控制if语句jinja2中的if语句类似与Python的if语句,它也具有单分支,多分支等多种结构,不同的是,条件语句不需要使用冒号结尾,而结束控制语句,需要使用endif关键字For循环jinja2中的for循环用于迭代Python的数据类型,包括列表,元组和字典。在jinja2中不存在while循环 在for循环中,jinja2还提供了一些特殊的变量,用以来获取当前的遍历状态:变量描述loop.index当前迭代的索引(从1开始)loop.index0当前迭代的索引(从0开始)loop.first是否是第一次迭代,返回boolloop.last是否是最后一次迭代,返回boolloop.length序列中的项目数量loop.revindex到循环结束的次数(从1开始)loop.revindex0到循环结束的次数(从0开始)3 jinja2注释{{# 这是注释 #}}4 Set语句模板中变量的值似乎只有一个来源,就是来自于后端的提供。但是实际上我们可以在模板这个层面对变量进行赋值,就是通过了{% set variable_name="value" %}的形式。这赋予了模板在内部进行变量赋值的能力,提高了灵活性。也可以set一个变量的值为某个函数的返回值二.jinja2中的过滤器 变量可以通过“过滤器”进行修改,过滤器可以理解为是jinja2里面的内置函数和字符串处理函数。 常用的过滤器有:过滤器名称 说明 safe 渲染时值不转义capitialize 把值的首字母转换成大写,其他子母转换为小写 lower 把值转换成小写形式 upper 把值转换成大写形式 title 把值中每个单词的首字母都转换成大写 trim 把值的首尾空格去掉 striptags 渲染之前把值中所有的HTML标签都删掉join 拼接多个值为字符串 replace 替换字符串的值 round 默认对数字进行四舍五入,也可以用参数进行控制int 把值转换成整型 那么如何使用这些过滤器呢? 只需要在变量后面使用管道(|)分割,多个过滤器可以链式调用,前一个过滤器的输出会作为后一个过滤器的输入。 三.jinja2的宏 宏类似于Python中的函数,我们在宏中定义行为,还可以进行传递参数,就像Python中的函数 在宏中定义一个宏的关键字是macro,后面跟其 宏的名称和参数等四.jinja2的继承和Super函数jinja2中最强大的部分就是模板继承。模板继承允许我们创建一个基本(骨架)文件,其他文件从该骨架文件继承,然后针对自己需要的地方进行修改。jinja2的骨架文件中,利用block关键字表示其包涵的内容可以进行修改。以下面的骨架文件base.html为例: 这里定义了四处 block,即:head,title,content,footer。如何继承修改注意看下面的文件 其他没修改的地方则继承PS: super()函数 表示获取block块中原来定义的内容。
-
随着人们生活水平的提高,人们对娱乐的需求也越来越高,影视需求就是其中之一。近些年来,我国的影视动画产业发展势头迅猛,《西游记之大圣归来》、《哪吒之魔童降世》、《白蛇·缘起》、《捉妖记》等优秀影视作品比比皆是。然而这些影视作品精彩之一就是特效,具有美感的特效场景才能带给用户极致的观感体验。说到特效,那么渲染行业发展至今,是什么样的趋势呢?未来又将如何?影视动画行业已然进入发展快车道,由此,对软件、硬件要求更高,对公有云更依赖;数字多媒体行业随着大尺寸普及,8K-10K增多,对精度要求更高;其他行业也对云服务要求也逐步增多;用户定制化需求量逐渐递增, 需匹配用户本地环境及需求。挑战和机遇并存,渲云与华为云深入合作,laas层基于成熟的华为云公有云平台模式,基于公有云传输协议,提升传输效率,加强传输安全,保障硬核储存资源,适应企业云时代的发展变化。渲云云桌面渲染上云价值:渲云云桌面渲染方案上云价值:渲云客户案例:《唐人街探案2》累计票房突破33亿,整部影片一共500多个特技镜头,涉及复杂的三维镜头有200多个。渲云在电影后期制作过程中提供了高效的渲染解决方案与技术服务,为其调配了大规模高端渲染服务器,为电影的项目周期提供了充分保障。《捉妖记2》不仅是第一部在剧情上的延续,还有全面升级的特效制作,从第一部的1200个特效镜头升级到为1800个特效镜头。渲云配合制作方的要求,以专业化的服务团队、大规模高性能渲染数据中心来确保项目按期按质完成,高速的渲染能力应对后期庞大的渲染量,游刃有余。当然,渲云云渲染不仅可以运用在影视动画领域,还可以运用在建筑设计、VR/AR、互动游戏、工业设计等多种领域,帮助用户快速完成三维内容、动画及效果图的渲染计算。未来,期望渲云继续与华为云深耕云渲染领域,为大众提供更多优秀的渲染场景!直播回放:《CG行业上云,不烧钱也能做特效大片》渲云云桌面渲染服务:https://marketplace.huaweicloud.com/contents/4e9ec5b4-06c7-438b-8e79-725b92195674?marketplace_live_20210629
-
关于“渲云”我的了解有如下:渲云客户端是一款面向三维设计师的自助式云渲染工具,轻松调动海量服务器资源,帮您快速完成渲染。渲云现已支持corona渲染器、vray渲染器,支持通道渲染、多镜头提交、光子渲染、分享结果文件等功能,可完全满足用户使用需求。渲云与多家全球知名公有云平台达成深度合作,实现海量节点拓展,提供全球性极速渲染服务, 用户遍及亚洲,欧美,中东等区域,为全球用户带来极致非凡的云渲染解决方案服务。下面有几个问题需要解答:渲云平台支持各种元素输出以及V-Ray Frame buffer校色吗?如何判断渲染文件是否在正常渲染?C4D渲染丢帧怎么办
-
当提起《斗罗大陆》、《唐人街神探》、《金刚川》、《三生三世十里桃花》等影视作品时,你的脑海里是不是浮现出酷炫的特效和精彩绝伦的CG动画?▼ *以上影视作品均由赞奇科技(渲云)参与渲染一部好的电影离不开好的导演、编剧、演员对于动画、科幻等特效要求高的电影作品来说,电影渲染技术尤为重要在建筑设计、VR/AR、互动游戏、影视动漫、工业设计等多领域三维内容制作高效渲染也是必不可少的环节▼ 本期直播邀请国内渲染行业领航者-赞奇科技CEO金伟为大家解锁如何基于华为云公有云,GET快速、高效、稳定的渲染新姿势!有奖直播:《CG行业上云,不烧钱也能做特效大片》(点我)直播嘉宾:赞奇科技CEO——金伟Kim直播时间:6月29日(周二)19:00-20:00推荐严选产品:渲云云桌面渲染服务(低至0.18元 /核/小时)渲云直播好礼1:问卷闯关,渲云速览点击直播页面向下拉,点击“问答闯关”按钮获得答卷(点我)填写华为云ID并且答题(5题),得分在75分以上且前500名提交的用户即可获得500码豆了解渲云,探索影视后期制作的秘密!(小云提示:所有问题的答案可在渲云官网(点我)找到答案哦!)了解什么是华为云码豆?(点我) (扫我加入社群)本次活动所有奖励领取,必须加入社群并收看直播,否则无效!群内添加群主小云微信——还可获得渲云独家[3dmax]资料包一份!渲云直播好礼2:畅谈渲染,赢华为云码豆&无门槛渲染券作为“设计狮”,渲云的资深用户又或者是首次接触渲云,对CG动画、三维设计感兴趣在这其中,对渲云的使用心得,对渲云的了解,“CG狗”的心路历程在本帖回复#渲云渲染#+一切关于“渲云”的议题且不少于50字便可获300码豆,活动结束后,将评选出4条最佳畅谈,赠送5000码豆+100元无门槛渲染券(一个id仅为一个畅谈可获码豆,但可提多个畅谈参与评选,限前500名)。例:(1.使用心得:#渲云渲染#偶然从朋友口中得知渲云这款云渲染软件,我就使用了一段时间,感受是真的很不错,无论是软件界面、安装程序大小、操作等方面都十分贴合设计师的需求,简单流畅,体验nice。2.对渲云的了解:#渲云渲染#赞奇科技成立于2010年,致力于三维视觉计算云技术研发与应用,目前完全自主研发并运营云渲染平台,为影视、动漫、建筑、室内室外、游戏、工业设计等用户提供云渲染服务以及其他相关的云PaaS、SaaS服务。)(扫我加入社群)本次活动所有奖励领取,必须加入社群并收看直播,否则无效!群内添加群主小云微信——还可获得渲云独家[3dmax]资料包一份!渲云直播好礼3:直播提问,四轮抽奖,“幸运鹅”就是你直播观看地址,点击沾好运!!1.直播间提问:直播期间在“问答区”向赞奇科技CEO金董提问,将抽取问题进行答疑,被选中第1-3个问题获得100元“渲云”无门槛渲染券,第4-5个问题获得“渲云”超大定制限量鼠标垫,第6-7个问题获得“呆猫”桌面云100元代金券,第8-10个问题获得5000码豆!!! (问题问得好,福利少不了!)(提问格式:手机号码后四位+问题,例:8898+哪里有海量渲染优惠体验券让我**?如何花低价钱制作出高大上的CG成图?)一定要来看直播,锁定6/29日晚上19:00!!!(点我)2.直播间抽奖:渲云直播好礼4:转发朋友圈,GET 渲云独家“3dmax模型包”向上拉获得本次直播宣传海报,转发至朋友圈(不少于2小时),并附上文字:【锁定6月29日19:00渲云&华为云直播,CEO亲临直播间为你讲解渲云渲染的100种姿势,海量100元渲染券、呆猫桌面云100元体验券、华为云精美好礼送送送!”直播地址:http://t.cn/A6VsA73Q】添加小云微信(huaweiyun1120),备注“直播”凭借转发截图,即可获得由渲云独家提供的“3dmax模型包”一份大量素材,绝对是你成为大神路上的好帮手!(扫我加入社群)本次活动所有奖励领取,必须加入社群并收看直播,否则无效!群内添加群主小云微信——还可获得渲云独家[3dmax]资料包一份!6月29日(周二)19:00,关于CG行业的所有秘密,都在直播间为你揭晓!>>>好礼直播传送门华为云码豆怎么用?会员中心入口:https://devcloud.huaweicloud.com/bonususer/home码豆奖励活动规则: 1)码豆可在码豆会员中心兑换实物礼品; 2)码豆奖励将于活动结束后的3个工作日内充值到账,请到会员中心的“查看明细”中查看到账情况; 3)码豆只能用于会员中心的礼品兑换,不得转让,具体规则请到会员中心阅读“码豆规则”; 4)为保证码豆成功发放,如果修改过账号名还请向工作人员提供修改前后的账号名。本期活动已结束,现将获奖用户公布如下:注:1、所有中奖用户在申诉时间结束前必须完成实名认证并确定华为云账号是否正确,否则码豆无法发放,视为放弃领奖。2、本次活动申诉时间:2021年7月6日16:00前1、最佳畅谈&直播间提问最佳畅谈直播提问楼层华为云账号码豆序号华为云账号码豆12#chenlingjun50008 lenghanzz500029#DouDou_50009hw21012767500036#canfen500010hw30141920500045#hw9454516150002、问答闯关&畅谈问答闯关论坛畅谈您的华为云ID是?码豆华为云账号码豆hw23597877500hw09974076300aahph1994500liuxifeng300hw02428460500a565855829300hwid_c7gbgw8wvl56bxs500zhengxx6688300无500hw_008617750345773_01300sunxiaobei500yizhangl300hw79284301500nukinsan300爹哥zzzdiego500hw46425134300zhienna500hw11326648300starry6500chenlingjun300violetevergarden123500hw86105405300亡魂陌路500hw55101971300zjj8008500kasson_300hw06979116500hw61299517300ljh752093278500hw46433192300xj120141121500hw26536804300hjh15003396976500hw78949957300HUAWEI_Yuan-Heng-Li-Zhen500lhw79459023-300liuyuanxiang500whw43454189_300hw17383096500meet00300sunshine_huawei500oznar119300shenhaodong500JaneConan300franco52576500DouDou_300hw36175659500hw63394802300awake_demon500xianuan300wuvita500hw36175659300hw68852324500canfen300xianuan500franco52576300skyfirecloud500hw75771608300Lisdon500hw73261881300cai_hy500hw94545161300wanggui001500qiushu2020300jockerxin500shenhaodong300hw96458811500sunshine_huawei300DouDou_500hw75435701300JaneConan500linghz666300oznar119500hw11078500hw10996515500hw61299517500linghz666500hw86105405500fuhaozhijia500whw43454189_500nukinsan500yizhangl500hw26536804500hw46433192500hw56223614500hw00922381500lhw79459023-500hw46425134500chenlingjun500suncker500hw79926743500hw_008617750345773_01500hw31711871500hw35544043500zhangpaodan500zhangyibaluobo500heiyouyoubaluobo500hw75771608500emptyBox500hw48382267500bigdata1500hw94545161500hw73261881500zhengxx6688500a565855829500qiushu2020500canfen500hw11326648500注:1、所有中奖用户在申诉时间结束前必须完成实名认证并确定华为云账号是否正确,否则码豆无法发放,视为放弃领奖。2、本次活动申诉时间:2021年7月6日16:00前3、请继续关注7月8日云市场直播最新一期直播《目睹一下,企业营销新模式》(点我),更多好礼,更多精彩!
-
Gatsby 项目结构建议使用 Starter 修改着理解 Gatsby,我用的是 Gatsby + Netlify CMS Starter。完整的 Gatsby 项目结构可以看文档,这里针对搭建博客用到的功能说明一下。 /src/pages 目录下的组件会被生成同名页面。 /src/templates 目录下放渲染数据的模板组件,如渲染 Markdown 文章,在其它博客系统中一般叫 layout。 /src/components 一般放其它共用的组件。 /static 放其它静态资源,会跳过 Webpack 直接复制过去。接下来是两个比较常用的配置文件,需要修改时参考 Starter 改即可。 /gatsby-config.js 基本用来配置两个东西: siteMetadata 放一些全局信息,这些信息在每个页面都可以通过 GraphQL 获取到。 plugins 配置插件,这个按用到时按该插件文档说明弄即可。 /gatsby-node.js 可以调用 Gatsby node APIs干一些自动化的东西。一般有两个常用场景: 添加额外的配置,比如为 Markdown 文章生成自定义路径。 生成 /src/pages 以外的页面文件,如为每个 Markdown 文章生成页面文件。 此外还有两个不那么常用的配置文件。 /gatsby-browser.js 可以调用 Gatsby 浏览器 APIs,一般插件才会用到,如滚动到特定位置。 /gatsby-ssr.js 服务器渲染的配置,一般也是插件才用到。 这就是搭建 Gatsby 博客的基本结构了,可以看到非常简单,且因为其丰富的生态,其它底层接口基本不需要用到。但接下来还是会有一些小坑,第一个便是 GraphQL,我们将马上来分析。为什么用 GraphQL 在上一节介绍了选择 Gatsby 的原因,其中提到了 Gatsby 使用 GraphQL 。大家可能会有疑惑,不是建静态博客么,怎么会有 GraphQL?难道还要部署服务器? 其实这里 GraphQL 并不是作为服务器端部署,而是作为 Gatsby 在本地管理资源的一种方式。 通过 GraphQL 统一管理实际上非常方便,因为作为一个数据库查询语言,它有非常完备的查询语句,与 JSON 相似的描述结构,再结合 Relay 的 Connections 方式处理集合,管理资源不再需要自行引入其它项目,大大减轻了维护难度。带魔法的 GraphQL 这里也是 Gatsby 的第一个坑。在 Gatsby 中,根据 js 文件的位置不同,使用 GraphQL 有两种形式,且 Gatsby 对其做了魔法,在 src /pages 下的页面可以直接 export GraphQL 查询,在其它页面需要用 StaticQuery 组件或者 useStaticQuery hook。 这里面查询语句虽然写的是字符串,但其实这些查询语句不会出现在最终的代码中,Gatsby 会先对其抽取。 个人其实不太喜欢魔法,因为会增加初学者的理解难度。但不得不承认魔法确实很方便,就是用了魔法的项目应该在文档最显眼的地方说明一遍。快速上手 GraphQL GraphQL 结构跟最终数据很相似,基本语法也非常简单,看看官方文档即可。一个快速上手的方式是访问项目开发时(默认 http://localhost:8000)的 /___graphql 页面,通过 GraphiQL 编辑器右侧可以浏览所有能够查询的资源。 另一个需要理解的是 Relay 的 Connections 概念,你会发现 Gatsby 里所有的数据集合都是以这种方式查询。推荐阅读 Apollo 团队分享的文章。 对 Connections 细致的理解往往是实现分页等底层需求时才需要,而这些均有插件完成。一般使用时只需要知道集合里每个项目的数据在 edges.node 中,同时通过 GraphiQL 浏览其它可以使用的数据。如对于 Markdown 文章,相应插件提供了字数统计以及阅读时长等数据,均可通过 GraphQL 直接获取。Debug GraphQL Gatsby 魔法带来的另外一个坑是 GraphQL 报错信息不全,可能会默默被吞掉,也可能无法定位到最终文件。 我在修改 starter 时踩到一个坑是复制组件时忘了修改 static query 查询语句的名称,导致重名报错。 避免错误最好方式是在 GraphiQL 编辑器中写好运行无误再复制到组件中。Remark 插件坑 Gatsby 中处理 markdown 最常用也是默认的插件是 gatsby-transformer-remark。这个插件对 markdown 文件解析后会生成 MarkdownRemark GraphQL 节点,其中 front matters 数据也会被解析出来。同时 MarkdownRemark 的集合对应为 allMarkdownRemark connections。 对于 connections 节点我们一般可以用 sort 和 filter 来筛选处理数据(可在 GraphiQL 编辑器中浏览),这里有一个坑便是如果要处理 front matters 数据,它们必须存在所有查询的 markdown 文件上并且具有相同的类型,插件才会生成相应的 fields,否则可能会抛出异常或者更糟糕的,默默失败了。 避免方式同上,先在 GraphiQL 编辑器中运行一遍,看看筛选的结果是否正确。 另外一种处理方式是在 /gatsby-node.js 中通过 onCreateNode 钩子,在生成 markdown 相关节点时手工处理,确保节点存在。 这在实现草稿和上下篇的时候会用到,具体例子我会在后续章节中提到。为什么选择 Netlify CMS 搭建 Gatsby 博客其实不需要 CMS 都是可以的,编写 Markdown 然后 build 即可。但这么做还是略嫌不便,通过 CMS 一般可以在一个可视化的在线环境中编辑文章,然后一键即可发布。 Gatsby 主流的两个 CMS 是 Contentful 和 Netlify CMS。 对于 Contentful 来说,文章是放在 Contentful 的服务器上的,管理也是通过 Contentful 提供的工具。当然其质量还是不错的,喜欢的可以参照官方的教程搭建。 Netlify CMS 是跟项目一起发布的,默认是在 /admin 页面下。文章也是存在源项目中,就是原来默认的 Markdown 文件。Netlify CMS 借助 Oauth 把写好的 Markdown 文件推送到项目源码的仓库上,再配合 Netlify 检测仓库变动自动构建发布。当然后者也不是必须的,可以换其它方式自动构建。 Netlify CMS 的优点是开源免费,文章跟项目源码在一起,界面可以高度自定义,甚至可以自行扩充 React 组件,基本满足简单的博客编写需求。配置 Netlify CMS 如果用官方的 starter配置将会非常简单。此 starter 默认使用 Github 作为仓库,Netlify 作为自动构建服务器。配置 Widgets 默认的 /static/admin/config.yml 已经配置好了大部分,如果对文章 Markdown 添加了自定义的 front matters 则需要再做些细调。 Widgets 代表了在 CMS 中可输入的模块,官方为常见的类型都提供了默认的 widgets ,没有满足的也可以自定义。 如我的博客中每篇文章都有一个 quote 域放些引用文字,那么在配置中添加上 fields: - label: "Quote" name: "quote" widget: "object" fields: - {label: "Content", name: "content", widget: "text", default: "", required: false} - {label: "Author", name: "author", widget: "string", default: "", required: false} - {label: "Source", name: "source", widget: "string", default: "", required: false} 如此即可在 CMS 中填写相关信息。配置预览 CMS 中提供了文章预览界面,如果需要自定义只需修改 /src/cms/ 下相应的文件即可,就是简单的 React 组件。 以上便是 Netlify CMS 最常用的配置,只需简单的修改博客现在就能跑起来了。接下来我们会通过实现草稿模式和上下篇文章来深入理解 Gatsby 的机制。 迁移博客需要考虑的一个重要问题便是路径兼容。我们当然不希望迁移后原有的链接无法访问,这不仅影响到 SEO ,更带来了不好的用户访问体验。本文将聊聊怎么让 Gatsby 兼容 Jekyll 式路径。Gatsby 如何生成特定页面 一般来说,在 /src/pages/ 目录下的组件会自动生成相应路径的页面,但如果是其它类型的文件就不会了。我们可以通过 Gatsby 的 Node APIs 来生成特定页面。 在 /gatsby-node.js 中配置 Gatsby Node APIs,如果项目是基于 starter 的话你很可能会发现里面已经有相应的配置。 我们通过声明 exports.createPages 钩子来配置页面生成,在回调中通过调用 actions.createPage 来生成某个指定页面。这个方法接受一个配置参数,其中的 path 域代表了生成页面的路径。 exports.createPages = ({ actions, graphql }) => { actions.createPage({ path, // ... }) }指定博客页面 那么我们怎么知道该生成哪些页面呢?这里通过 exports.createPages 回调中的 graphql 来查询 Markdown 文件。 exports.createPages = ({ actions, graphql }) => { const { createPage } = actions return graphql(` { allMarkdownRemark(sort: { order: ASC, fields: [frontmatter___date] }) { edges { node { id fields { slug } frontmatter { path title layout } } } } } `).then(result => { if (result.errors) { result.errors.forEach(e => console.error(e.toString())) return Promise.reject(result.errors) } // ... }) } Netlify CMS 会在 Markdown front matters 中的 path 域生成路径。根据默认的 /static/admin/config.yml 我们的路径应该是 /blog/{{year}}-{{month}}-{{day}}-{{slug}}/ ,这个可能跟旧博客不一样,如 Jekyll 是 /{{year}}/{{month}}/{{day}}/{{slug}}/ 。修改 Markdown 节点 在 Remark 插件生成的 Markdown 节点中,我们可以往 fields 域放一些自定义的变量。这里我们把自定义的路径存到 fields.slug 中。 通过 /gatsby-node.js 中的 exports.onCreateNode 钩子我们可以在生成节点的时候进行拦截处理。你可能发现文件里面已经有一些配置的代码了,我们这里只关注 Markdown 相关的。 exports.onCreateNode = ({ node, actions, getNode }) => { const { createNodeField } = actions if (node.internal.type === `MarkdownRemark`) { // Jeykll style post path const filepath = createFilePath({ node, getNode }) createNodeField({ node, name: 'slug', value: filepath.replace( /^\/blog\/([\d]{4})-([\d]{2})-([\d]{2})-/, '/$1/$2/$3/' ) }) } } 我们把原有的路径值换成了自定义值并存在了 fileds.slug 中。创建页面 回到我们前面的查询,得到需要的数据之后只需要对每个页面调用 actions.createPage 即可。 exports.createPages = ({ actions, graphql }) => { const { createPage } = actions return graphql(` { allMarkdownRemark(sort: { order: ASC, fields: [frontmatter___date] }) { edges { node { id fields { slug } frontmatter { path title layout } } } } } `).then(result => { if (result.errors) { result.errors.forEach(e => console.error(e.toString())) return Promise.reject(result.errors) } const { edges } = result.data.allMarkdownRemark const options = edges.map(edge => ({ path: edge.node.fields.slug, title: edge.node.frontmatter.title, component: path.resolve( `src/templates/${edge.node.frontmatter.layout}.js` ), // additional data can be passed via context context: { id: edge.node.id } })) options.forEach(option => createPage(option)) }) } 也许你会问为什么不在这里直接计算自定义路径而是要存到 fields.slug 中。这是因为这个路径我们可能还会在其它地方用到,存起来就不必多处计算了。 上面代码中可以注意到还有个 context 域,这个域中的数据会被传到 component 的 props 中。这样我们在模板组件中通过 pageContext.id 便可判断当前渲染的文件。 通过实现自定义路径基本上可以了解 Gatsby 页面生成的方式了。下节中我会继续谈谈其它个性化的配置,如草稿模式和显示上下篇博文。草稿模式 草稿模式即可以将文章保存为草稿而不被渲染出来。方式是在 front matters 中设置一个 draft 布尔域,以此域作为渲染参考。 坑 这里有一个地方需要注意,前面文章提过,Markdown 插件需要所有文章中都有 draft 域且都是布尔类型才会生成相应的 GraphQL 查询。如果是新的博客这个问题不大,如果是迁移过来的,有两个解决方式,第一个是手动写个脚本给文章都补上域,另一个是利用 Gatsby 的 Node APIs 在 fields 上生成特定域,鲁棒性更好些。自动生成域 观察 Remark 插件生成的 GraphQL 类型,我们可以发现,front matters 都被放在 frontmatter 域中,而与之同级的有一个前面文章提到过的 fields 域,用来放自定义生成的数据。 Gatsby 在生成 GraphQL 节点时提供了钩子 onCreateNode,我们利用这个钩子往 fields 中放自定义的数据。 编辑 /gatsby-node.js ,如果是用了 starter 的话这里很可能已经有其它的代码,已有的不需要动,添加我们需要的即可。 exports.onCreateNode = ({ node, actions, getNode }) => { const { createNodeField } = actions if (node.internal.type === `MarkdownRemark`) { createNodeField({ node, name: 'draft', value: Boolean(node.frontmatter.draft) }) } } 如此 fields 中就保证了会有 draft 这个域了。过滤草稿 有了标记之后,在生成页面的地方我们就需要过滤草稿。 首先是普通的文章页面生成,这个是在 createPages 钩子中,如果你的博客只有文章用到 Markdown 的话,可以在 GraphQL 查询中直接过滤,否则我们用前面文章的方法,先取所有 Markdown 文件再根据渲染的模板来分别处理各种类型的文章。 注意我把模板域的名字换成了自己更习惯的 layout,原来的 starter 中应该叫 templateKey。修改其实也很简单,搜索所有文件替换关键字即可。 options .filter( (_, i) => !( edges[i].node.frontmatter.layout === 'blog-post' && edges[i].node.fields.draft ) ) .forEach(option => createPage(option)) 我在主页中也列举了最近的几篇文章,这里也需要过滤草稿,可以直接在 GraphQL 中过滤。 query IndexQuery { latestPosts: allMarkdownRemark( sort: { order: DESC, fields: [frontmatter___date] } filter: { fields: { draft: { ne: true } } frontmatter: { layout: { eq: "blog-post" } } } limit: 5 ) { edges { node { excerpt(pruneLength: 200) id fields { slug } frontmatter { title description layout date(formatString: "MMMM DD, YYYY") } } } } } 其它地方同理。上下篇 在文章页面中我们通常会加入上下篇来引导继续浏览。这里我们同样在 createPages 钩子中处理,但这回我们添加到 context 域中,这个域里的数据会作为 props 传到模板组件中。 在 createPage 生成文章页面前添加处理代码计算上下篇: options .filter( (_, i) => edges[i].node.frontmatter.layout === 'blog-post' && !edges[i].node.fields.draft ) .forEach((option, i, blogPostOptions) => { option.context.prev = i === 0 ? null : { title: blogPostOptions[i - 1].title, path: blogPostOptions[i - 1].path } option.context.next = i === blogPostOptions.length - 1 ? null : { title: blogPostOptions[i + 1].title, path: blogPostOptions[i + 1].path } }) 然后在文章的 /src/templates/blog-post.js 组件里,接收 pageContext props,就可以使用上面传入的数据了。这是我的例子。 通过实现这几个功能我们了解了 Gatsby 页面生成的方式以及其 Node APIs 的基本使用。Gatsby 的功能远不止这些,官方文档写得非常详细,需要实现其它功能建议先去看看有无现有的例子。本系列到这里暂告一段落,谢谢你的阅读,希望能对你搭建 Gatsby 博客有所帮助。
推荐直播
-
HDC深度解读系列 - Serverless与MCP融合创新,构建AI应用全新智能中枢2025/08/20 周三 16:30-18:00
张昆鹏 HCDG北京核心组代表
HDC2025期间,华为云展示了Serverless与MCP融合创新的解决方案,本期访谈直播,由华为云开发者专家(HCDE)兼华为云开发者社区组织HCDG北京核心组代表张鹏先生主持,华为云PaaS服务产品部 Serverless总监Ewen为大家深度解读华为云Serverless与MCP如何融合构建AI应用全新智能中枢
回顾中 -
关于RISC-V生态发展的思考2025/09/02 周二 17:00-18:00
中国科学院计算技术研究所副所长包云岗教授
中科院包云岗老师将在本次直播中,探讨处理器生态的关键要素及其联系,分享过去几年推动RISC-V生态建设实践过程中的经验与教训。
回顾中 -
一键搞定华为云万级资源,3步轻松管理企业成本2025/09/09 周二 15:00-16:00
阿言 华为云交易产品经理
本直播重点介绍如何一键续费万级资源,3步轻松管理成本,帮助提升日常管理效率!
回顾中
热门标签