前端 – 一只橙梓一个窝 https://blog.orangii.cn 可能会长出二叉树? Mon, 26 Feb 2024 03:30:29 +0000 zh-CN hourly 1 https://wordpress.org/?v=6.5.5 https://static.orangii.cn/avatar/logo.png 前端 – 一只橙梓一个窝 https://blog.orangii.cn 32 32 React Native元素定位问题 https://blog.orangii.cn/2023/react-native-measure-view/ https://blog.orangii.cn/2023/react-native-measure-view/#comments Sun, 29 Oct 2023 15:51:20 +0000 https://blog.orangii.cn/?p=766 在Web上,获得一个元素的位置信息可以使用 getBoundingClientRect 方法获得,但是在RN的View上,就没有这样的方法了,需要使用RN View的专有方法:measure

measure

measure的使用需要使用ref获取View(或其他原生RN容器,这里只能是原生RN的容器)引用,然后调用上面的measure方法,这个方法接受一个回调函数,参数分别为 x、y、width、height、pageX、pageY参数(参数按照顺序列举)。

但是如果元素实际未渲染(例如Flatlist中不在屏幕中没有渲染的元素),则无法获得它的位置。

import { useRef, FC } from 'react';
import { ScrollView, View, useWindowDimensions } from 'react-native';

export const TextComp: FC = (props) => {
  const viewRef = useRef<View>(null);
  const { width: windowHeight } = useWindowDimensions();
  const handleScroll = () => {
    viewRef.current?.measure?.((x, y, width, height, pageX, pageY) => {
      if (pageY < windowHeight) {
        console.log('View Appears');
        // ...
      }
    });
  }
  
  return (
    <ScrollView
      style={{ paddingVertical: 1000 }}
      handleScroll={handlesScroll}
      scrollEventThrottle={16}
    >
      <View ref={useRef}> ... </View>
    </ScrollView>
  );
}

可能遇到的问题

在安卓中,一个View如果只用来包含子元素,而不存在任何跟空间有关的属性(例如style、className、onLayout等),那么这个View会被安卓RN底层优化,从而导致这个View在Component Tree上并没有挂载。虽然不会影响页面视觉效果,但是会导致measure返回不可控的数值。

为了防止容器被优化,需要用到 collapsable 属性,例如下面的代码:

import { useRef, FC } from 'react';
import { ScrollView, View, useWindowDimensions } from 'react-native';

export const TextComp: FC = (props) => {
  const viewRef = useRef<View>(null);
  const { width: windowHeight } = useWindowDimensions();
  const handleScroll = () => {
    viewRef.current?.measure?.((x, y, width, height, pageX, pageY) => {
      if (pageY < windowHeight) {
        console.log('View Appears');
        // ...
      }
    });
  }
  
  return (
    <ScrollView
      style={{ paddingVertical: 1000 }}
      handleScroll={handlesScroll}
      scrollEventThrottle={16}
    >
      <View 
        ref={useRef}
        collapsable={false} // <---
      > ... </View>
    </ScrollView>
  );
}

除了上述方法外,使用Animated.View、为View添加style属性或者onLayout回调都会防止它被安卓RN优化掉。

参考链接:software-mansion/react-native-reanimated#3188

]]>
https://blog.orangii.cn/2023/react-native-measure-view/feed/ 6
我的第一个npm包 – ovx,一个方便生成项目基础的工具 https://blog.orangii.cn/2022/my-npm-package-ovx/ https://blog.orangii.cn/2022/my-npm-package-ovx/#comments Fri, 13 May 2022 08:21:02 +0000 https://blog.orangii.cn/?p=540 已经?了很长时间了,最近除了考试就是大作业,马上要考研了,也没有办法。

不过最近因为有一个讲座报名小程序的项目,我要写一个后台,因为时间短(任务不重),规模也不大,就想到了用Vue,不过搭建项目的过程如果要每次都自己操作一次的话,就太慢了,于是根据平常常用的东西,制作了一个整合npm工具包:ovx

使用方法也非常的简单,下面两个方法都可以:

// npx
npx ovx@latest

// npm global
npm i ovx@latest -g // pnpm also work
ovx

目前支持Vue 3 + Vite 2 + vue router 4为基础,Ant Design/Element Plus为UI库的模板,支持SFC写法及其的自动导入,即可以不使用import语句直接使用UI库的组件;也支持TSX(defineComponent或者函数式组件,不过函数式组件传递给vue-router时会出现一点点问题)。

ovx使用截屏(大文件GIF)

至于为什么不使用普通JavaScript而一定要用TypeScript,因为TS很香,写了TS就不想写JS了,不然完全不知道某个对象具体有什么属性方法。

ovx生成的项目结构
生成的项目结构

更多的模板还在添加,欢迎大家来Github提建议或问题。

最后,这个包也使用了Github Action来保证可用性及更新依赖,不得不说Github Action真的是太方便了,现在在Github发布一个Release就能自动发布最新包,非常的方便。

如果需要React框架,也可以试试我学长的脚手架 → yu7
]]>
https://blog.orangii.cn/2022/my-npm-package-ovx/feed/ 6
Vue 3 Setup标签的使用笔记,及一次Vue-ts的经历 https://blog.orangii.cn/2022/vue-setup-and-vuets/ https://blog.orangii.cn/2022/vue-setup-and-vuets/#comments Wed, 19 Jan 2022 14:08:35 +0000 https://blog.orangii.cn/?p=376 最近想起来之前买的域名 huli.li 一直没有加内容,想至少要加一些内容,于是就去Codepen上面找灵感,碰巧就找到了一个白天黑夜切换的CSS小狐狸,就想到判断当前位置是否日落来切换小狐狸的白天黑夜状态,说干就干。

在网上搜了一些API,最终使用腾讯地图的API来通过IP获取经纬度位置(没有通过浏览器自带的Geolocation首先是支持度的问题,其次还需要用户同意和一段时间的定位,会比较麻烦),日出日落的API使用的是和风天气的API。项目使用的是Vite.js创建的Vue-ts项目。

以上为项目仓库,后面将不时引用一下,具体引用版本为v0.2.2

setup标签和setup()函数内容基本相同,一下就仅说setup标签了

Vue 3 Setup 标签的使用

官方文档

一般默认的Vue项目的script标签都是这样的:

<script>
export default{
// ...
}
</script>

Vue 3 的setup标签可以直接不适用export default,直接在script标签里写内容并暴露给组件,而且不像之前的写法,数据在data()里、方法在methods里、钩子在另外的地方,写一个功能就要改三个地方,setup标签就可以很好的解决这个问题。

setup标签就是简单的把script标签上加上setup属性<script setup></script>

在setup标签中定义组件props

普通情况下需要使用props: string[] | object来定义Props,在setup标签中只需要用defineProps

const props = defineProps({ foo: String });

const props = defineProps<{foo: string}>(); // TS专用写法,尖括号里是interface

在项目仓库里查看:/src/components/Fox.vue Line 44-49(带默认值的方法),/src/components/Clock.vue L19,L21(不带默认值的方法)

在setup标签中使用响应式变量

以前要想创建响应式变量,需要找到data然后去加或者修改,现在可以在setup标签的任意地方使用ref来添加响应式变量:

import { ref } from 'vue'

const num = ref(0) // 函数参数为默认值,不传递则为undefined
const weather = ref<IWeather>(); // TS写法,尖括号传入变量类型

在项目仓库中查看:/src/App.vue L13-L15

在setup标签中使用生命周期的钩子

以往使用生命周期钩子就是直接在对应的生命周期函数的地方进行重写,而现在则是使用组合式API生命周期函数,例如beforeUnmounted:

import { onMounted } from 'vue';

onMounted(() => console.log('Mounted'));

在项目仓库中查看案例:/src/App.vue L59-L63

值得一提的是,在setup标签中的内容将会在组件创建时运行,也就是说组合式API生命周期函数没有beforeCreate和created的对应API,因为在setup标签中的语句的执行时期和它们一样(setup围绕beforeCreated和created运行)。setup中生命周期钩子对应表请看:https://v3.cn.vuejs.org/guide/composition-api-lifecycle-hooks.html

使用TypeScript的原因

首先肯定TS是之后的趋势,多学习一下肯定是好的;然后就是……之前写项目TS用太多了导致面对返回体那么大的和风API不敢写了,就比如res.now.obsTime,如果不写好interface的话,写下一个”.”后面IDE是不会有提示和文档的,这样让我不太敢继续”点“下去,不知道对象到底会不会有这个属性,有了TS的话各种格式都非常的清晰,并且IDE也会有代码提示和文档,还有可能是空对象的报错,写起来也更放心。

项目中写的一个weather的interface:/src/data/interfaces/weather.ts,IDE效果:

IDE文档提示
鼠标移到上方的代码提示

使用Github Action自动构建并发布

之前就总想试试Github Action的自动构建,这次在网上查了一些资料,自己写了一个脚本,可以在仓库的这个文件看到。

不过还是遇到了一点问题,本地安装jQuery和和风天气图标的时候只给保存到了node_modules但是没保存到package.json里,导致在运行 npm install 之后总是缺少依赖导致构建失败,索性在安装依赖的时候同时执行安装命令一并安装了。/.github/workflows/main.yml L22-L24

 - name: Installing dependencies packages
        run: |
          npm install
          npm install jquery
          npm i qweather-icons
文章发布后半小时我解决了这个问题,因为在安装依赖时没有加-S,正确应该是 npm i jquery -S

最终成果

在一些小的版本迭代后,最终有了现在的版本,这个版本有以下特性

  • 获取当前的IP位置、天气和日出日落
  • 每小时更新一次天气
  • 小狐狸会在日出时醒来,日落后睡觉
  • 日出日落如果跨天会自动更新

还加了一些控制台信息,主要是一些版权,如下

如果想要体验的话可以直接访问:https://huli.li/,也可以顺手给咱的仓库点个Star~

]]>
https://blog.orangii.cn/2022/vue-setup-and-vuets/feed/ 8
React学习笔记,及与Vue的对比(一) https://blog.orangii.cn/2022/react-vs-vue-1/ https://blog.orangii.cn/2022/react-vs-vue-1/#comments Fri, 14 Jan 2022 08:09:54 +0000 https://blog.orangii.cn/?p=354 不说别的我们上来直接来一波对比,通过Vite.js分别创建不同预设的示例项目,看一下源代码进行对比:

虽然二者都是通过拼接自定义的组件来构成页面,但是可以看得出来差别是非常大的(字面上和意义上),Vue更像是在写HTML,而React却像是在写JSP、PHP之类的页面(但是又不完全相同)。因此Vue相对于新手来说更容易上手,因为他的数据data、方法method、和一些生命周期的钩子都是写在<script>标签里,并且分别需要写在对应位置上,开发小型项目相对简单。

Vue格式举例:

<!-- Demo.vue -->
<tempalte>
  <div id="root">
    <!-- Component template -->
  </div>
</template>

<script>
export default {
  name: 'Demo',
  data () {
    return {
      count: 0,
    }
  },
  methods: {
    // ...
  },
  mounted () {
    // ...
  },
</script>

<style scoped>
  #root {
    display: flex;
  }
</style>

如果想要引用组件只需要import xxx from './Demo.vue';相对来说写法简单容易理解,因此一般学习前端框架都会第一推荐学Vue,一般学过三件套(HTML、CSS、JS)都可以读官方文档学会大部分的用法。

到此Vue就说完了,简单易学。然后来说说React。


React的格式就很奇怪,如果你再看一次上面的示例图片,可能会看到很多不太能理解的东西:

React JSX

首先如果你对ES6的东西没又很多的学过,可能对const [count, setCount] = useState(0);这句有些疑惑,这是ES6开始有的解构,具体可以看菜鸟教程之类的内容学习。还有就是HTML被作为函数返回值返回了,而且不是以字符串的形式返回,这是因为JSX的存在,JSX可以直接将HTML解析成ReactNode,但是JSX就像less一样需要编译器(Babel)进行编译后才可以正常像JS一样在浏览器中运行。

React的组件可以使用class类集成React.Component来创建组件,组件的内容要写在render函数中,因为这种方式可能导致混淆需不需要this、state管理很奇怪等问题,我们在写项目的时候很少使用了,这里就贴一个React官方的“井字棋”小游戏作为演示。

一般来说还可以把组件直接写成一个函数,并且将要渲染的内容return回去,里面的state、固定方法都需要使用React Hook进行定义。关于React Hook后面再详细记录。一个React组件格式示例:

// Demo.tsx (or Demo.jsx if you are not using ts)
import { useState, useEffect } from 'react';

export const Demo = () => {
  const [count, setCount] = useState(0);
  useEffect(() => console.log('Did mounted'));

  return (
    <>
      <h1>Hello World</h1>
      <h2>Hello React</h2>
    </>
  );
};

需要注意的是,如果return的元素超过一行,需要使用括号()包裹,以避免解析成为其他内容;return的元素类似Vue2的组件,只能包含一个根节点,但是与Vue不同的是它可以使用空标签<></>来表示一个节点用来包裹其他内容,如上面的内容所示。(<></>如果不必须可以省略)。

在Vue中,在HTML中插入需要动态更新的内容需要使用双大括号{{}}写法,但是在React中需要使用大括号包裹js代码,然后在js中返回一个ReactNode。例如

<!-- Vue -->
<h1>{{ post.title }}</h1>
<p v-html="post.content"></p>
// React
return (
  <>
    <h1>{ post.title }</h1>
    <section>{ marked(post.content) }</section>
  </>
);

在Vue中的循环也只需要v-for就可以实现,但是对于React就需要使用array.map来进行循环(但是二者都通过给定的key来判断是否是统一列表),例如:

<!-- Vue -->
<ul>
  <li v-for="post in postList" key="post.id">
    {{ post.title }}
  </l1>
</ul>
// React
return (
  <ul>
    {
      postList.map(post => 
        <li key={post.id}>{post.title}</li>
      )
    }
  </ul>
);

之后还有其他很多东西,例如HTML元素的很多属性在转换为ReactNode之后名字会有一些变化,例如onclick -> onClick、class -> className,之类的,在这里没有说,但是一般写组件的时候都会写文档说明传入什么参数,或者利用TypeScript规定props的interface,所以使用时传入的属性值都是根据文档来的。

哦对,对于组件的属性props,Vue和React差距也是比较大的,React是将props作为参数传给组件的函数/构造函数,简单做个举例:

// Vue
export default {
  props: ['prop1', 'prop2'],
  // ...
};

// 或指定类型以提供文档
export default {
  props: {
    count: Number,
    title: String,
  },
};
// React with typescript TSX
// 使用 TypeScript Interface 确定参数类型以达到提供文档和阻止传入错误的参数
export interface PropsInterface {
  count: number;
  title: string;
  content?: string;
}

export const DemoWithProps = (props: PropsInterface) => {
  // ...
}

暂时先记录这么多,后面还会继续记录学习TypeScript和React的学习笔记,各种React Hook在文章中有些示例使用了但是没有说明,在后面再做记录。

]]>
https://blog.orangii.cn/2022/react-vs-vue-1/feed/ 3