你可以这样子去理解useReducer

发表于 2年以前  | 总阅读数:316 次

本文将聚焦于useReducer这个钩子函数的原理和用法,笔者带领大家再一次深入认识useReducer。

前言

相信前期使用过react的前端同学,大都会经历从class 语法向hooks 用法的转变,react 的hooks 编程给我们带来了丝滑的函数式编程体验,同时很多前端著名的文章也讲述了hooks 带来的前端心智的转变,这里就不再着重强调,本文则是聚焦于useReducer这个钩子函数的原理和用法,笔者带领大家再一次深入认识useReducer。

useReducer是在react V 16.8 推出的钩子函数,从用法层面来说是可以代替useState,众所周知,useState常用在单个组件中进行状态管理,但是遇到状态全局管理的时候,useState 显然不能满足我们的需求,这个时候大多数的做法是利用第三方的状态管理工具,像redux,Recoil或者Mobx,在代码里就会有:


import XXX from Mobx;
import XXX from Redux;
// or
import XXX from Recoil;

这些三方的import 语句。强大的react团队难道就不能自己实现一个全局的状态管理的hook嘛,这不,useReducer 为了解决这个需求应运而生。

虽然有了useReducer,但是黄金法则依旧成立:组件的状态交给组件管理,redux负责工程的状态管理。本文则负责讲解useReducer是如何执行全局的状态管理,并且什么时候用合适,什么时候不合适,这里也会提及。

useReducer是如何发挥作用的

在学习一个新特性的时候,最好的方式之一是首先熟悉该特性的原理,进而可以促进我们的学习。

useReducer 钩子用来存储和更新状态,有点类似useState钩子。在用法上,它接收一个reducer函数作为第一个参数,第二个参数是初始化的state。useReducer最终返回一个存储有当前状态值的数组和一个dispatch函数,该dispatch函数执行触发action,带来状态的变化。这其实有点像redux,不过还是有一些不同,后面笔者会列举这两个概念和不同。

关于reducer函数

通常的,reduce方法在数组的每一个元素上都执行reducer函数,并返回一个新的value,reduce方法接收一个reducer函数,reducer函数本身会接收4个参数。下面这段代码片段揭示一个reducer是如何运行的:


const reducer = (accumulator, currentValue) => accumulator + currentValue;
[2, 4, 6, 8].reduce(reducer)
// expected output: 20

在React中,useReducer接收一个返回单组值的reducer函数,就像下面这样:

const [count, dispatch] = useReducer(reducer, initialState);

前面提到过,这里的reducer函数本身会接受两个参数,第一个是state,第二个是action,这个action会被dispatch执行,就像是:

function reducer(state, action) { }

dispatch({ type: 'increment' })

根据不同的action ,reducer函数会带来不同的state的变化,就像是 type 是 increment的情况,reducer函数会使得state 加1。

懒惰创建初始state

在编程概念中,懒初始化是延迟创建对象的一种手段,类似于直到被需要的第一时间才去创建,还有其他的动作比如值的计算或者高昂的计算开销。正如上面提到的,useReducer 的第三个参数是一个可选值,可选的懒创建state的函数,下面的这段代码是更新state的函数:

const initFunc = (initialCount) => {
    if (initialCount !== 0) {
        initialCount=+0
    }
  return {count: initialCount};
}

// wherever our useReducer is located
const [state, dispatch] = useReducer(reducer, initialCount, initFunc);

当initialCount变量不为0的时候,赋值为0;并返回count的赋值对象。注意第三个参数是一个函数,并不是一个对象或者数组,函数中可以返回对象。

dispatch函数

dispatch函数是触发不同action的函数,通常的它是接受含有type的一个对象,并根据这个type来执行对应的action,action执行完成之后,render函数继续发挥作用,这时候会更新state。当我们关注的焦点不在useReducer用法细节上时,我们会在宏观上看到render和state的变化过程。

组件触发的action都是接收含有type 和 payload的对象,其中type代表不同action的区别,payload是action将要添加到state的数据。

在使用上,dispatch用起来非常的简单,就拿JSX语法来讲,可以直接在组件事件上触发action操作,代码如下:


// creating our reducer function
function reducer(state, action) {
  switch (action.type) {
   // ...
      case 'reset':
          return { count: action.payload };
    default:
      throw new Error();
  }
}

// wherever our useReducer is located
const [state, dispatch] = useReducer(reducer, initialCount, initFunc);

// Updating the state with the dispatch functon on button click
<button onClick={() => dispatch({type: 'reset', payload: initialCount})}> Reset </button>

注意到,reducer函数接收payload作为传参,其中这个payload是来自dispatch的贡献,初始化的state也是会影响payload的。组件之间,使用props传递数据的时候,其实dispatch也是直接可以封装在函数中,这样方便的从父组件将dispatch传递到子组件,就像下面这样:

<Increment count={state.count} handleIncrement={() => dispatch({type: 'increment'})}/>

在子组件中,接收props,触发的时候,则有:

<button onClick={handleIncrement}>Increment</button>

不触发dispatch

如果useReducer返回的值和当前的一样,React不会更新组件,也不会引起effect的变化,因为React内部使用了Object.is 的语法。

useState vs useReducer

相信阅读React官方文档学习的同学,第一个接触的Hook就是useState,useState是一个基础的管理state变化的钩子,对于更复杂的state管理,甚至全局的state管理,useReducer是用来干这件事情的。然而,useState其实是使用到useReducer的,这意味着,只要是使用useState实现的,都可以使用useReducer去实现。

但是呢,这两个钩子useReducer 和 useState还是有不同的,在用useReducer的时候,可以避免通过组件的不同级别传递回调。useReducer 提供dispatch在各个组件之间进行传递,这种方式提高了组件的性能。

然而,这并不意味着每一次的渲染都会触发useState函数,当在项目中有复杂的state的时候,这时候就不能用单独的setter函数进行状态的更新,相反的你需要写一个复杂的函数来完成这种状态的更新。因此推荐使用useReducer,它返回一个在重新渲染之间不会改变的 dispatch 方法,并且您可以在 reducer 中有操作逻辑。还值得注意的是,useState最后是触发的update 来更新状态,useReducer 则是用dispatch来更新状态。

接下来我们来看这两种钩子函数:useState 和 useReducer 是如何声明和使用的。

用useState 声明state

useState 的声明语句非常的简单,例如:

const [state, setState] = useState('default state');

useState 返回一个保存当前state和更新state的数组,这里的setState是更新state的函数。

用useReducer 声明state

使用useReducer 的时候看下面的语句:

const [state, dispatch] = useReducer(reducer, initialState)

useReducer 返回一个保存当前state和一个更新state的dispatch函数。这个dispatch函数有点类似setState,我们在用setState更新state的时候,是这样用:


<input type='text' value={state} onChange={(e) => setState(e.currentTarget.value)} />

在onChange事件中调用setState更新当前的state。对比使用useReducer 钩子,可以这样表达:

<button onClick={() => dispatch({ type: 'decrement'})}>Decrement</button>

这里的语意是当用户点击按钮的时候,会触发dispatch,执行type是decrement的action。另外在使用dispatch函数我们还可以传payload:


<button onClick={() => dispatch({ type: 'decrement',payload:0})}>Decrement</button>

我们知道useReducer 可以处理复杂多层state的情况,这里笔者继续举该类情况的例子:

const [state, dispatch] = useReducer(loginReducer,
  {
    users: [
      { username: 'Philip', isOnline: false},
      { username: 'Mark', isOnline: false },
      { username: 'Tope', isOnline: true},
      { username: 'Anita', isOnline: false },
    ],
    loading: false,
    error: false,
  },
);

useReducer 接收一个初始对象,对象的key包含users,loading,error。使用useReducer 管理本地state的方便之处是用useReducer 可以改变部分的state,也就是说,这里可以单独改变users。

什么时候该使用useReducer呢?

在开发项目的时候,随着我们工程的体积不断的变大,其中的状态管理会越来越复杂,此时我们最好使用useReducer。useReducer 提供了比useState更可预测的状态管理。当状态管理变的复杂的时候,这时候useReducer 有着比useState 更好的使用体验。

这里的不得不重提一个法则:当你的state是基础类型,像number,boolean,string等,这时候使用useState是一种更简单、更合适的选择。

下面笔者将创建一个登陆的组件,让读者体会使用useReducer的好处。

创建一个登陆组件

为了让我们更好的理解useReducer 的用法,这里创建一个登陆组件,并比较一下使用useState 和 useReducer 在状态管理用法上的异同。

首先我们先用useState创建登陆组件:


import React, { useState } from 'react';

export default function LoginUseState() {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const [isLoading, showLoader] = useState(false);
  const [error, setError] = useState('');
  const [isLoggedIn, setIsLoggedIn] = useState(false);
  const onSubmit = async (e) => {
    e.preventDefault();
    setError('');
    showLoader(true);
    try {
      await function login({ username, password }) {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            if (username === 'ejiro' && password === 'password') {
              resolve();
            } else {
              reject();
            }
          }, 1000);
        });
      }
      setIsLoggedIn(true);
    } catch (error) {
      setError('Incorrect username or password!');
      showLoader(false);
      setUsername('');
      setPassword('');
    }
  };
  return (
    <div className='App'>
      <div className='login-container'>
        {isLoggedIn ? (
          <>
            <h1>Welcome {username}!</h1>
            <button onClick={() => setIsLoggedIn(false)}>Log Out</button>
          </>
        ) : (
          <form className='form' onSubmit={onSubmit}>
            {error && <p className='error'>{error}</p>}
            <p>Please Login!</p>
            <input
              type='text'
              placeholder='username'
              value={username}
              onChange={(e) => setUsername(e.currentTarget.value)}
            />
            <input
              type='password'
              placeholder='password'
              autoComplete='new-password'
              value={password}
              onChange={(e) => setPassword(e.currentTarget.value)}
            />
            <button className='submit' type='submit' disabled={isLoading}>
              {isLoading ? 'Logging in...' : 'Log In'}
            </button>
          </form>
        )}
      </div>
    </div>
  );
}

对于username,password,isLoading等的管理,都是使用的useState进行的处理,所以这里我们使用了五个useState钩子函数,面对更多的state的时候,有时候我们会担心我们是否可以更好的管理这些state呢。这时候可以尝试用useReducer,直接在reducer 函数中管理全部的状态。


import React, { useReducer } from 'react';

function loginReducer(state, action) {
  switch (action.type) {
    case 'field': {
      return {
        ...state,
        [action.fieldName]: action.payload,
      };
    }
    case 'login': {
      return {
        ...state,
        error: '',
        isLoading: true,
      };
    }
    case 'success': {
      return {
        ...state,
        isLoggedIn: true,
        isLoading: false,
      };
    }
    case 'error': {
      return {
        ...state,
        error: 'Incorrect username or password!',
        isLoggedIn: false,
        isLoading: false,
        username: '',
        password: '',
      };
    }
    case 'logOut': {
      return {
        ...state,
        isLoggedIn: false,
      };
    }
    default:
      return state;
  }
}
const initialState = {
  username: '',
  password: '',
  isLoading: false,
  error: '',
  isLoggedIn: false
};
export default function LoginUseReducer() {
  const [state, dispatch] = useReducer(loginReducer, initialState);
  const { username, password, isLoading, error, isLoggedIn } = state;
  const onSubmit = async (e) => {
    e.preventDefault();
    dispatch({ type: 'login' });
    try {
      await function login({ username, password }) {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            if (username === 'ejiro' && password === 'password') {
              resolve();
            } else {
              reject();
            }
          }, 1000);
        });
      }
      dispatch({ type: 'success' });
    } catch (error) {
      dispatch({ type: 'error' });
    }
  };
  return (
    <div className='App'>
      <div className='login-container'>
        {isLoggedIn ? (
          <>
            <h1>Welcome {username}!</h1>
            <button onClick={() => dispatch({ type: 'logOut' })}>
              Log Out
            </button>
          </>
        ) : (
          <form className='form' onSubmit={onSubmit}>
            {error && <p className='error'>{error}</p>}
            <p>Please Login!</p>
            <input
              type='text'
              placeholder='username'
              value={username}
              onChange={(e) =>
                dispatch({
                  type: 'field',
                  fieldName: 'username',
                  payload: e.currentTarget.value,
                })
              }
            />
            <input
              type='password'
              placeholder='password'
              autoComplete='new-password'
              value={password}
              onChange={(e) =>
                dispatch({
                  type: 'field',
                  fieldName: 'password',
                  payload: e.currentTarget.value,
                })
              }
            />
            <button className='submit' type='submit' disabled={isLoading}>
              {isLoading ? 'Logging in...' : 'Log In'}
            </button>
          </form>
        )}
      </div>
    </div>
  );
}

在使用useReducer 代替useState的过程中,我们会发现useReducer会使我们更聚焦于type和action,举个例子说,当执行login动作的时候,会将isLoading,error 和 state进行赋值:


case 'login': {
      return {
        ...state,
        error: '',
        isLoading: true,
      };
    }

体验好的一点是,我们再也不需要主动去更新state,useReducer 的赋值会直接帮助我们解决所有的问题。

其他例子

  • 最小化的范式

且看下面最简单的例子:

const initialState = 0;
const reducer = (state, action) => {
  switch (action) {
    case 'increment': return state + 1;
    case 'decrement': return state - 1;
    case 'reset': return 0;
    default: throw new Error('Unexpected action');
  }
};

代码很简单,首先定义一个初始化的state:initialState = 0;之后在reducer函数中通过switch 来对state执行不同的操作。注意到,这里的state其实是个number 对象,这在Redux的使用者看来或许有一些疑惑,因为在redux中都是用object来处理的。这其实是useReducer的方便之处。

在组件中,常常会有点击事件带来状态变化的情况,比如说购物车组件中商品数量的增加,点击加号商品数量会加一,这个时候上面的代码就可以应用到组件中,例如:


const Example01 = () => {
  const [count, dispatch] = useReducer(reducer, initialState);
  return (
    <div>
      {count}
      <button onClick={() => dispatch('increment')}>+1</button>
      <button onClick={() => dispatch('decrement')}>-1</button>
      <button onClick={() => dispatch('reset')}>reset</button>
    </div>
  );
};

当用户点击+1的按钮时,dispatch会出发increment的action,count +1 ,所以会看到state变化后的结果。这种type其实可以定义很多,选择合适的数量即可。

  • action 对象

下面的例子其实有点像redux的用法,习惯redux的同学可能会比较熟悉:

const initialState = {
  count1: 0,
  count2: 0,
};
const reducer = (state, action) => {
  switch (action.type) {
    case 'increment1':
      return { ...state, count1: state.count1 + 1 };
    case 'decrement1':
      return { ...state, count1: state.count1 - 1 };
    case 'set1':
      return { ...state, count1: action.count };
    case 'increment2':
      return { ...state, count2: state.count2 + 1 };
    case 'decrement2':
      return { ...state, count2: state.count2 - 1 };
    case 'set2':
      return { ...state, count2: action.count };
    default:
      throw new Error('Unexpected action');
  }
};

初始化的state是一个对象,并且return 出去的也是一个对象。和前面的那个例子相比,除了多了不同的case之外,在更新state通过对象赋值的方式进行。initialState 对象中是有两个key,在更新的时候针对指定的key更新即可。上面的例子看起来有些复杂,把它用到组件上,会简化使用过程:

const Example02 = () => {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      <div>
        {state.count1}
        <button onClick={() => dispatch({ type: 'increment1' })}>+1</button>
        <button onClick={() => dispatch({ type: 'decrement1' })}>-1</button>
        <button onClick={() => dispatch({ type: 'set1', count: 0 })}>reset</button>
      </div>
      <div>
        {state.count2}
        <button onClick={() => dispatch({ type: 'increment2' })}>+1</button>
        <button onClick={() => dispatch({ type: 'decrement2' })}>-1</button>
        <button onClick={() => dispatch({ type: 'set2', count: 0 })}>reset</button>
      </div>
    </>
  );
};

Example2 组件中,上半部分显示的是count1 的变化,下半部分则是显示count2的变化。也是通过点击button来触发dispatch,引起state变化。

在文本框组件中使用

前面的两个例子都是通过button上面的onClick事件来触发,在平时的业务开发中,输入框组件的onChange事件也是我们常使用的方法,此时我们也可以结合useReducer来结合输入框的value属性使用,做到实时展示输入的内容,使得组件受控,见下面的代码:


const initialState = '';
const reducer = (state, action) => action;

const Example03 = () => {
  const [firstName, changeFirstName] = useReducer(reducer, initialState);
  const [lastName, changeLastName] = useReducer(reducer, initialState);
  return (
    <>
      <div>
        First Name:
        <TextInput value={firstName} onChangeText={changeFirstName} />
      </div>
      <div>
        Last Name:
        <TextInput value={lastName} onChangeText={changeLastName} />
      </div>
    </>
  );
};

当我们在TextInput 组件中自定义onChangeText 方法,这个时候通过 changeFirstName 函数,改变changeFirstName值,进而改变value值。

  • 结合useContext使用

在日常的开发中,组件之间共享state的时候,很多人使用全局的state,虽然这样可以满足需求,但是降低了组件的灵活性和扩展性,所以更优雅的一种方式是使用useContext,对于useContext不熟悉的同学可以参考react官方文档关于这一部分的讲解。在本例子中,笔者将使用useContext 和 useReducer 函数一起使用,看下面的代码:


const CountContext = React.createContext();

const CountProvider = ({ children }) => {
  const contextValue = useReducer(reducer, initialState);
  return (
    <CountContext.Provider value={contextValue}>
      {children}
    </CountContext.Provider>
  );
};

const useCount = () => {
  const contextValue = useContext(CountContext);
  return contextValue;
};

useCount 函数是自定义的hook,和正常的hook使用的方式是一致的。那么组件在使用useCount 钩子的时候,可以像下面这样用:


const Counter = () => {
  const [count, dispatch] = useCount();
  return (
    <div>
      {count}
      <button onClick={() => dispatch({ type: 'increment' })}>+1</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-1</button>
      <button onClick={() => dispatch({ type: 'set', count: 0 })}>reset</button>
    </div>
  );
};

// now use it
const Example05 = () => (
  <>
    <CountProvider>
      <Counter />
      <Counter />
    </CountProvider>
    <CountProvider>
      <Counter />
      <Counter />
    </CountProvider>
  </>
);

useCount 会走内部的useReducer,这个时候通过dispatch函数会改变对应的state的状态。

  • 订阅的需要

Context使用的场景其实是在组件之间,但是如果在组件的外部,这个时候我们需要使用订阅来做。这个时候我们可以订阅一个共享的state,并当state更新的时候去更新组件。对于前面的那个使用Context的例子,这里我们用订阅实现一下。

第一步,首先写一个最简单的useReducer:

const useForceUpdate = () => useReducer(state => !state, false)[1];

接下里写一个函数创建共享的state并返回一个钩子函数:


const createSharedState = (reducer, initialState) => {
  const subscribers = [];
  let state = initialState;
  const dispatch = (action) => {
    state = reducer(state, action);
    subscribers.forEach(callback => callback());
  };
  const useSharedState = () => {
    const forceUpdate = useForceUpdate();
    useEffect(() => {
      const callback = () => forceUpdate();
      subscribers.push(callback);
      callback(); // in case it's already updated
      const cleanup = () => {
        const index = subscribers.indexOf(callback);
        subscribers.splice(index, 1);
      };
      return cleanup;
    }, []);
    return [state, dispatch];
  };
  return useSharedState;
};

这里我们使用了useEffect钩子函数,在这个钩子函数中,我们订阅一个回调函数来更新组件,当组件卸载的时候,我们也会清除订阅。

接下来我们创建两个共享的state:

const useCount1 = createSharedState(reducer, initialState);
const useCount2 = createSharedState(reducer, initialState);

用一下这个钩子函数:

const Counter = ({ count, dispatch }) => (
  <div>
    {count}
    <button onClick={() => dispatch({ type: 'increment' })}>+1</button>
    <button onClick={() => dispatch({ type: 'decrement' })}>-1</button>
    <button onClick={() => dispatch({ type: 'set', count: 0 })}>reset</button>
  </div>
);

const Counter1 = () => {
  const [count, dispatch] = useCount1();
  return <Counter count={count} dispatch={dispatch} />
};

const Counter2 = () => {
  const [count, dispatch] = useCount2();
  return <Counter count={count} dispatch={dispatch} />
};

最后我们用一个函数组件封装Counter:


const Example06 = () => (
  <>
    <Counter1 />
    <Counter1 />
    <Counter2 />
    <Counter2 />
  </>
);

这里的count的更新都是使用共享的useCount钩子函数。

什么时候不该使用useReducer呢?

这是一个好的问题,前面介绍了使用useReducer 的情况,但是什么时候我们不可以用useReducer 呢。

为了更好的理解这个问题,笔者首先说一下使用useReducer 基本的心智,useReducer 是可以帮助我们管理复杂的state , 但是我们也不应该忽略redux在某些情况下可能是更好的选择。

最开始我们的想法是我们尽量避免使用第三方的state管理工具,当你有疑惑是否要使用他们时,说明这不是用他们的时候。

下面笔者列举几个使用Redux和Mobx的例子。

当你的应用需要单一的来源时

当前端的应用通过接口获取数据,且这个数据源就是从这个接口获取的,这个时候使用Redux 可以更方便的管理我们的state,就像是写一个todo/undo demo,直接可以使用Redux。

当你需要一个更可预测的状态

当你的应用运行在不同的环境中时,使用Redux可以使得state的管理变得更稳定。同样的state和action传到reducer的时候,会返回相同的结果。并且redux不会带来副作用,只有action会使其更改状态。

当状态提升到顶部组件

当需要在顶部组件处理所有的状态的时候,这时候使用Redux 是更好的选择。

总结

到这里useReducer 的使用姿势和用法例子讲解都已经介绍完成了,最后我们回顾一下,首先类比于redux的reducer,useReducer 的思路和redux一样,不同点是在于useReducer 最终操作的对象是state。在使用上,就拿最简单的button组件为例子,点击的时候触发dispatch,根据type修改state。复杂一点的,可以结合useContext使用,满足多个组件共享state的情况。

总之,在掌握用法之后,多在项目中实践,learn by doing , 是较为有效的掌握知识的方式。

本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/LkazEzUBvpsubM9fXU9TOQ

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 相关文章
Android插件化方案 5年以前  |  237227次阅读
vscode超好用的代码书签插件Bookmarks 2年以前  |  8063次阅读
 目录