前端 JS 异常那些事

前言

人无完人,所以代码总会出异常的,异常并不可怕,关键是怎么处理

什么是异常

程序发生了意想不到的情况,影响到了程序的正确运行

从根本上来说,异常就是一个普通的对象,其保存了异常发生的相关信息,比如错误码、错误信息等。以 JS 中的标准内置对象 Error 为例,其标准属性有 message。许多宿主环境额外增加了 filename 和 stack 等属性

错误只有被 throw,才会产生异常,不被抛出的错误不会产生异常。比如直接new Error()甚至打印 Error 但是不 throw,也是不会产生异常

异常的分类

编译时异常

源代码在编译成可执行代码之前产生的异常,无需执行即有异常。编译、语法解析发生错误。编译型语言对于这种很常见的,但是解析型的 js 也是会有编译型异常。通常是非合法的 js 语句、ts 编译报错

console.log(1)

let 1 // Uncaught SyntaxError: Unexpected number



function test() {

    console.log(1)

    await 1

}

代码本身不会执行就抛异常,不会处理到打印 1 的阶段。这种情况通常不会有实际影响,因为 babel/ts 等工具处理时就会直接报错。除非不经编译直接写代码,例如有时候我们直接写在 html 中写的一些代码

运行时异常

代码被执行之后产生的异常。这些通常是很难提前发现的,因为代码实际运行中会遇到。比较常见的如TypeError: Cannot read properties of undefined这样的读取了undefined的属性。运行时异常对比编译时异常的特点是代码执行到异常代码前都是会正常执行的

执行到a.b.c前的打印能成功,异常抛出后后面的语句就不能执行了。运行时异常即可是这种引擎层面抛出的也可以是代码手动抛出的

而上面说的编译时异常,即使异常语句前的正常语句也是不会执行

异常传播

异常抛出就像事件冒泡一样具有传递性。如果一个异常没有被 catch,它会沿着函数调用栈一层层传播直到栈空。

异常会不断传播直到遇到第一个 catch。 如果都没有捕获,会抛出类似 unCaughtError,表示发生了一个异常,未被捕获的异常通常会被打印在控制台上

error 对象

Error本身作为函数直接调用和被 new 调用的效果是一样的

const a = Error('a')

const b = new Error('b')

javascript 规范中总共有 8 中错误类型构造函数

  • Error – 错误对象
  • SyntaxError --解析过程语法错误(上面提到的编译时异常)
  • TypeError – 不属于有效类型(上面举例的运行时异常)
  • ReferenceError – 无效引用(严格模式下直接访问一个未定义的变量)
  • RangeError – 数值超出有效范围
  • URIError – 解析 URI 编码出错
  • EvalError – 调用 eval 函数错误
  • InternalError – Javascript 引擎内部错误的异常抛出, “递归太多”

Error 是错误的基类,其他类型都继承 Error 这个类

console.log(Object.getPrototypeOf(SyntaxError) === Error);    // true

console.log(Object.getPrototypeOf(TypeError) === Error);   // true

console.log(Object.getPrototypeOf(ReferenceError) === Error);   // true

console.log(Object.getPrototypeOf(RangeError) === Error);   // true

console.log(Object.getPrototypeOf(URIError) === Error);   // true

console.log(Object.getPrototypeOf(EvalError) === Error);   // true

默认的 error 对象只有一个 message 信息,很多时候对于错误的细分是很不好使,一般可以通过扩展这个错误对象,抛异常时抛出自定义的错误对象,在异常处理或时实现更精细化的处理

class ApiError extends Error {

  constructor(message, code) {

    super(message);

    this.code = code

  }

}



const err = new ApiError('xxx', 404)

err instanceof  ApiError

一种常见的应用就是在 axios 处理的异常中抛出一个扩展的 ApiError 对象,传递错误信息、错误等,在错误处理时对于这种错误进行特殊处理。如自定义上报、catch 住不作为 js 异常上报。不进行这种处理的话平时比较常见的情况就是会造成 slardar 的中 js 错误部分会有很多 axios 抛出的噪音

除了扩展错误对象,目前有一个处于 stage 4 的 Error Cause 提案 https://github.com/tc39/proposal-error-cause。这个提案也是由阿里推进的国内的首个es提案

Chrome 96 版本目前还不可用,firefox 可用

通过传递给 Error 构造函数的第二个参数一个 cause 属性为一个 Error 对象,即可看到是哪个错误具体产生当前的错误,对于一些调用链路比较深的可可能存在多个异常抛出情况这个特性还是相当好用的,可以准确追踪。Error Cause 当然用自定义扩展错误也能够实现这个功能

async function doJob() {
  const rawResource = await fetch('//domain/resource-a')
    .catch(err => {
      throw new Error('Download raw resource failed', { cause: err });

    });
  const jobResult = doComputationalHeavyJob(rawResource);
  await fetch('//domain/upload', { method: 'POST', body: jobResult })
    .catch(err => {
      throw new Error('Upload job result failed', { cause: err });
    });

}

try {
  await doJob();
} catch (e) {
  console.log(e);
  console.log('Caused by', e.cause);
}
// Error: Upload job result failed
// Caused by TypeError: Failed to fetch

Error 的相关 api

  • 改变堆栈帧数

默认情况下,V8 引发的几乎所有错误都具有一个 stack 属性,该属性保存最顶层的 10 个堆栈帧,格式为字符串 at xxx

Error.stackTraceLimit

Error.stackTraceLimit 属性指定堆栈跟踪收集的堆栈帧数。默认值为 10,可以设置为任何有效的 JavaScript 数值。 更改将影响值更改后捕获的任何堆栈跟踪。如果设置为非数字值,或设置为负数,则堆栈跟踪将不会捕获任何帧

  • 收集自定义异常
Error.captureStackTrace(error, constructorOpt)

这个 API 可以给自定义对象追加 stack 属性,达到模拟 Error 的效果,追加的 stack 表示调用 Error.captureStackTrace() 的代码中的位置的字符串。

function CustomError(message) {
  this.message = message;
  this.name = CustomError.name;
  Error.captureStackTrace(this); // 给对象追加stack属性

}

try {
  throw new CustomError('msg');
} catch (e) {
  console.log(e)
}

需要注意的是stack属性对于不同浏览器的格式是不一致的,通常而言监控 sdk 会统一做处理

这个方法支持传递一个constructorOpt参数,表示所有 constructorOpt 以上的帧,包括 constructorOpt,都将从生成的堆栈跟踪中省略。具体的差异如下

使用这个参数可以用于调用栈过深时隐藏深层次的一些调用细节

  • sourcemap 还原错误

还原错误也是利用了 error 对象的 stack 属性。可以使用stacktraceysource-map实现根据错误堆栈还原到实际发生错误的代码

线上代码经过压缩后一般只有 1 行,对于定位原始错误是很困难的。并且默认的e.stack属性是个字符串,可以借助stacktracey进行解析并结合source-map进行反解

const sourceMap = require('source-map');
const SourceMapConsumer = sourceMap.SourceMapConsumer;

const Stacktracey = require('stacktracey');
const errorStack = '...'; // 错误信息

const sourceMapFileContent = '...'; // sourcemap文件内容
const tracey = new Stacktracey(errorStack); // 解析错误信息

const sourceMapContent = JSON.parse(sourceMapFileContent);

const consumer = await new SourceMapConsumer(sourceMapContent);

for (const frame of tracey) { // 这里的frame就是stack中的一行所解析出来的内容
    // originalPosition不仅仅是行列信息,还有错误发生的文件 originalPosition.source

    const originalPosition = consumer.originalPositionFor({
        line: frame.line,
        column: frame.column,
    });

    // 拿到错误所对应的源码以及上面的行列号
    const sourceContent = consumer.sourceContentFor(originalPosition.source);
    console.log(sourceContent);

}
自动、手动抛出

异常可手动抛出也可自动抛出

自动抛出:代码执行报错由引擎抛出。这种由于逻辑缺失容错造成的自动抛出错误应该是要尽最大程度杜绝并防范的

const a = {}

a.b.c = 1

手动抛出:直接调用throw

那什么时候应该手动抛出异常呢?一个指导原则就是已经预知到程序不能正确进行下去了。

switch (type) {
    case a:
    break;
    case b:
    break;
    default:
     throw new Error('xxxx')

}
抛出异常还是返回特定错误信息

对于上面提到可预知的异常需要终止流程,也可以使用抛出异常或者返回特定数据来让调用方感知。

  • 抛出异常

好处,调用方无需判断返回值,抛出异常默认就不会走后面的逻辑代码了。常见于 axios 对于 code 非 0 的异常抛出处理并自定义上报。再结合上面提到的扩展 error 对象,可以在监控上报前判断属于特定错误不作为 js 上报,避免网络异常造成的 js 错误增加噪音

instance.interceptors.response.use(async (res: AxiosResponse<Result>) => {
    if (res?.data?.statusCode !== 0) {
        throw new ApiError('xx', res?.data?.statusCode)
    }
    return res

})
  • 不抛异常而是返回特定信息

如果上述的代码不抛出异常而是直接返回 res 的话,每一处调用就都要手动判断 code。接口 http 返回 http code 200 而响应体 code 不等于 0 也属于不抛异常而是返回特定信息的方式

const res = await api()
if (res.code !==0) {
    return
}

异常处理

同步、异步

try-catch 作为 JavaScript 中处理异常的一种标准方式,如果 try 块中的任何同步代码发生了错误,就会立即退出代码执行过程,然后执行 catch 块。此时 catch 块会接收到一个包含错误信息的对象。try-catch 使用时也可以搭配 finnally 使用。 finally 一经使用,其代码无论如何都会执行。对于异步调用可封装成 promise 的 catch 方法进行调用或借助 async/await 语法糖使用 try/catch

try {
 fn()
} catch(e) {}
fn().then(() =>{}, () => {
    // catch
})

fn().catch(() => {})

try { await fn() } catch(e) {}

可能到处 try catch 确实不是一种优雅的方式,可以进行适当的封装

  1. 对于异步 promise 调用可以直接使用await-to-js,利用 Promise 的特性,分别在 promise.then 和 promise.catch 中返回不同的数组,其中 fulfilled 的时候返回数组第一项为 null,第二个是结果。rejected 的时候,返回数组第一项为错误信息,第二项为 undefined。使用的时候,判断第一项是否为空,即可知道是否有错误
import to from 'await-to-js';
async function asyncTask() {

     let err, user, savedTask;
     [err, user] = await to(UserModel.findById(1));
     if(!user) throw new CustomerError('No user found');
     [err, savedTask] = await to(TaskModel({userId: user.id, name: 'Demo Task'}));

     if(err) throw new CustomError('Error occurred while saving task');

    if(user.notificationsEnabled) {
       const [err] = await to(NotificationService.sendNotification(user.id, 'Task Created'));
       if (err) console.error('Just log the error and continue flow');
    }
}
  1. 对于 class 方法调用适当使用装饰器进行 catch
export function CatchAsync(errorLabel = '') {
    return function (target: unknown, key: string, descriptor: PropertyDescriptor) {
        errorLabel = errorLabel || key
        const originFn = descriptor.value
        descriptor.value = async function (...rest: unknown[]) {
            try {
                return await originFn.call(this, ...rest)
            } catch (e) {
               // do something
                throw e
            }
        }
    }
}



export function CatchSync(errorLabel = '') {
    return function (target: unknown, key: string, descriptor: PropertyDescriptor) {
        errorLabel = errorLabel || key
        const originFn = descriptor.value
        descriptor.value = function (...rest: unknown[]) {
            try {
                return originFn.call(this, ...rest)
            } catch (e) {
                // do something
                throw e
            }
        }
    }
}



class A {
    @CatchAsync('1')
    async request() {
    }
}

Promise catch 小细节

以下两种写法的区别

then(f1,f2) vs then(f1).catch(f2)

绝大多数情况下是相同的。

区别在于第一种写法 f2 无法捕获 f1 中的异常。第二种写法 f2 能捕获 f1 中的异常

全局兜底

对于无需手动捕获或者没有捕获的异常最终会抛到全局。通过全局errorunhandledrejection进行监听并处理。监听全局异常和未捕获的 Promise 异常并进行相关处理

function onReject(e) {
    // ...
    report(e)

}

window.addEventListener('unhandledrejection', onReject, true)
window.addEventListener('error', this.onError, true)

window.onerrorwindow.addEventListener error的区别

  • window.onerror 函数返回 true 可以阻止执行默认事件处理函数(即控制台没有 error 打印出来)
  • window.addEventListener error若为捕获阶段,则可额外捕获静态资源的加载错误。window.onerror则无法捕获静态资源的加载错误

React 中的异常

白屏异常

React 处理阶段的同步代码报错,整个组件树挂了导致卸载掉,页面展示白屏

  • 生命周期函数报错
  • render 方法报错
  • 构造函数报错

上述提到的是同步代码报错,异步代码的报错是不会产生页面白屏,只是会产生一些 console 中的 error。同理,因为事件回调函数的处理不是在 React 处理阶段(初始化或者事件处理setState驱动 react 进行下次渲染的),所以事件处理函数中的报错同样不会触发白屏

Error Boundary

既然白屏问题如此严重,必须要有一种方式帮助开发者来感知 React 中的白屏问题。 于是 React16 就有了Error Boundary来用来捕获渲染时错误的概念,在 React 新增了两个生命周期componentDidCatchstatic getDerivedStateFromError用于捕获渲染时的错误,也仅能捕获上面提到的白屏异常(如异步错误等也是没有办法被捕获到),也就是说如果我们在Error Boundary中捕获到错误并上报,这个错误通常是非常严重的。Error Boundary只可用于捕获子组件中发生的异常(自身出现渲染错误也是无法捕获的)

无法捕获的异常

  • 事件处理

  • 异步代码(例如 setTimeoutrequestAnimationFrame 回调函数)

  • 服务端渲染

  • 它自身抛出来的错误(并非它的子组件)

  • componentDidCatch

用于出错时去执行的副作用代码,比如错误上报、错误兜底等

  • static getDerivedStateFromError

在出错后触发,改函数返回的值能进行 setState 更新,触发一次重新 render 来渲染错误时的 fallback 组件。如果这次渲染仍然出现渲染错误,页面仍然会白屏,而不是执行类似 render error -> getDerivedStateFromError -> render error 这样的死循环

class Demo extends Component {
    state = {
        error: false
    }

    static getDerivedStateFromError(){
        return {
            error: true
        }
    }

    componentDidCatch(error, info){
        this.setState({
            error: true
        })
    }



    render() {
        if (this.state.error) {
            return <div>error</div>
        }
        // Child中发生渲染错误

        return <div>
            <Child/>
        </div>

    }

}

`

  • static getDerivedStateFromError渲染阶段调用的,所以不允许出现副作用
  • componentDidCatch【commit】阶段被调用,所以允许出现副作用

目前 React 的 Error Boundary 提供的两个生命周期只存在于 class 组件;并没有相应的 hooks 能实现类似的功能

There are no Hook equivalents to the uncommon getSnapshotBeforeUpdate, getDerivedStateFromError and componentDidCatch lifecycles yet, but we plan to add them soon.

但是有一个比较有趣的是,Preact 提供了相应的 hook useErrorBoundary去实现 Error Boundary。preact 中的useErrorBoundary的功能和getDerivedStateFromErrorcomponentDidCatch是一模一样的

// error = The error that was caught or `undefined` if nothing errored.

// resetError = Call this function to mark an error as resolved. It's

//   up to your app to decide what that means and if it is possible

//   to recover from errors.

const [error, resetError] = useErrorBoundary();

用法也是非常简单,子组件触发异常会触发函数组件的 render 并且 error 是对应的错误信息,并且还提供了对应的 resetError 去重置错误。至于为何 Preact 能先于 React 支持功能,原因在于对于 Preact 的实现来说,它的函数组件和 class 组件都是实例化成一样的实例,函数组件的 hook 中直接定义componentDidCatch进行处理,componentDidCatch 捕获到错误后通过setState设置错误对象驱动下一次的 render 来拯救白屏

export function useErrorBoundary(cb) {

    const state = getHookState(currentIndex++, 10);

    const errState = useState();

    state._value = cb;

    if (!currentComponent.componentDidCatch) {

        currentComponent.componentDidCatch = err => {

            if (state._value) state._value(err);

            errState[1](err);

        };

    }

    return [

        errState[0],

        () => {

            errState[1](undefined);

        }

    ];

}

虽然这是一个 react 的 Error Boundary 只存在于 class 组件,但是对于子组件是函数组件的情况下,相关 hooks 的异常(useEffectuseLayoutEffect)一样是能捕获到的

实践

这么基础常用的 error-boundary 通常来说不需要我们手动去搞。开源社区已经有了成熟的封装解决方案react-error-boundary。它基于 React 提供的 error boundary 能力提供了开箱即用的功能,使用的时候只需要将我们的组件作为ErrorBoundary的子组件传入即可,并且 ErrorBoundary 还提供 FallbackComponent 属性供出错时渲染 fallback 内容、错误恢复等许多更进阶的功能。并且也提供了 HOC 的方式供使用

Error Boundary 包子组件
import {ErrorBoundary} from 'react-error-boundary'



function ErrorFallback({error, resetErrorBoundary}) {

  return (

    <div role="alert">

      <p>Something went wrong:</p>

      <pre>{error.message}</pre>

      <button onClick={resetErrorBoundary}>Try again</button>

    </div>

  )}



  const ui = (

  <ErrorBoundary

    FallbackComponent={ErrorFallback}

  >

    <ComponentThatMayError />

  </ErrorBoundary>)
高阶组件
import {withErrorBoundary} from 'react-error-boundary'

const ComponentWithErrorBoundary = withErrorBoundary(ComponentThatMayError, {

  FallbackComponent: ErrorBoundaryFallbackComponent,

  onError(error, info) {

    // Do something with the error

    // E.g. log to an error logging client here

  },

})

// or

@withErrorBoundary({
  FallbackComponent: ErrorBoundaryFallbackComponent,
  onError(error, info) {
    // Do something with the error
    // E.g. log to an error logging client here
  },

})

class ComponentThatMayError extends Component {}

const ui = <ComponentWithErrorBoundary />

在需要使用的地方对我们的组件进行一层包装即可。这时候可能会一种需求,手动包一层太麻烦了,为啥 react 不提供一个配置字段每个组件自带 error boundary 呢?

万能的开源社区也有人通过 babel 插件实现了这个能力babel-plugin-transform-react-error-boundary

{

    test: /.jsx?$/,

    exclude: /node_modules/,

    use: {

        loader: 'babel-loader',

        options: {

            plugins: [

                [ 'babel-plugin-transform-react-error-boundary', {

                    ErrorBoundary: 'common/components/ErrorBoundary/index.js'

                } ]

            ]

        }

    }

}

通过配置一个自定义的 Error Boundary 路径,即可实现所有的组件包一个 ErrorBoundary 了,再结合react-error-boundary一顿操作,页面再也不会白屏了。具体实现就是通过 babel 实现以下这样的转换

class TestComponent extends React.Component {

    render() {

        return <div />;

    }

}

// 转换

const ErrorBoundary = require('./path/to/my/ErrorBoundary.js');

class TestComponent extends React.Component {
    render() {
        return <ErrorBoundary>{this.__r__()}</ErrorBoundary>;
    }


    __r__) {
        return <div />;

    }

}

上面提到 Error boundaries是不支持 ssr 场景的,所以又有人做了一个针对 ssr 的 babel 插件

babel-plugin-transform-react-ssr-try-catch。 通过对 render 函数进行 trycatch 实现类似的功能

{
    "plugins": [

        ["react-ssr-try-catch", {

            // global errorHandler

            "errorHandler": "./path/to/my/SSRErrorHandler.js",

            // component error render method

            "errorRenderMethod": "renderErrorState",

        }]

    ]

}
class MyCompoenent extends React.PureComponent {

  render() {

    return <div/>;

  }

}

// 转换

const ReactSSRErrorHandler = require('./path/to/my/SSRErrorHandler.js');

class MyCompoenent extends React.PureComponent {

  render() {

      try {

          return this.__originalRenderMethod__();

      } catch (e) {

          return ReactSSRErrorHandler(e, this.constructor.name, this);

      }

  }



  __originalRenderMethod__() {

      return <div />;

  }

}
实现更多的功能

Error Boundary 除了用于捕获错误,这个特性也可以用来实现 React Suspense 相关的功能

  1. Suspense + Lazy
// 用法

const ProfilePage = lazy(() => import('./ProfilePage'));



<Suspense fallback={<Spinner />}>

  <ProfilePage />

</Suspense>
// 最简化实现Lazy + Suspense

function lazy(loader) {

    let prom;

    let component;

    let error;



    function Lazy(props) {

        if (!prom) {

            prom = loader();

            prom.then(

                exports => {

                    component = exports.default || exports;

                },

                e => {

                    error = e;

                }

            );

        }



        if (error) {

            throw error;

        }



        // Lazy组件的render中,若组件未加载完成,抛出一个promise异常供Suspense的componentDidCatch捕获

        if (!component) {

            throw prom;

        }



        return createElement(component, props);

    }

    return Lazy;

}



export class Suspense extends React.Component {

    state = {

        isLoading: false

    };



    componentDidCatch(error) {

        if (this._mounted) {

            if (typeof error.then === 'function') {

                this.setState({ isLoading: true });

                error.then(() => {

                    if (this._mounted) {

                        this.setState({ isLoading: false })

                    }

                });

            }

        }

    }

    componentDidMount() {

        this._mounted = true;

    }

    componentWillUnmount() {

        this._mounted = false;

    }





    render() {

        const { children, fallback } = this.props;

        const { isLoading } = this.state;



        return isLoading ? fallback : children;

    }

}
  1. Suspense + render 中的【同步】数据获取
const fetchApi = () => {
   // 异步api
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("数据");
        }, 3000);
    });

}



const useData = () => {
    const [data,setData] = useState()
    useEffect(() => {
        fetchApi().then((x) => setData(x))

    },[])
    return data

}

const ProfilePage = () => {
   const data = useData()
    if (!data) {
        return <Loading/>

    }

   return <div>{data}</div>

}
const fetchApi = () => {
   // 异步api
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("数据");
        }, 3000);
    });
}

const getData = createFetcher(fetchApi);

const ProfilePage = () => {

   const data = getData()

   return <div>{data}</div>

}



const Demo = () => {

    return <Suspense fallback={<Loading />}>

        <ProfilePage />

    </Suspense>

}
var cached = {};

export const createFetcher = (promiseTask) => {

    let ref = cached;

    return () => {

        if (ref !== cached) {

            return ref

        }

        const task = promiseTask();

        task.then(res => {

            ref = res

        });

        if (ref === cached) {

            throw task

        }

    }

}

Vue 中的异常

vue 提供了 4 个异常处理的 API,分别是 errorHandler,errorCaptured,renderError,warnHandler

errorHandler

我们最常用的是全局配置中注册的 errorHandler,例如异常上报场景,可用如下代码:

Vue.config.errorHandler = function (error, vm, info) {

    window.Slardar && window.Slardar('Sentry', (Sentry) => {

      Sentry.captureMessage(error);

    });

}

errorHandler 可以捕获 render(vue 模板)、生命周期钩子、watch 回调、methods 方法等函数内的同步代码异常,info 参数会接收到报错函数类型(render/mounted/…);如果这些函数返回 promise ,则 promise 异常也会被捕获;

errorCaptured

errorCaptured 入参和 errorHandler 一样,它是 vue 组件的钩子函数,作用是捕获来自后代组件(注意不包含本组件)的错误。 vue 中的错误传播规则可以总结为,从子到父传播,依次触发各组件的 errorCaptured 钩子,若某 errorCaptured 返回 false,则停止传播,否则最终会传播到全局的 errorHandler

使用场景:我们可以在组件库等场景使用 errorCaptured,捕获内部异常并上报,从而避免和业务代码报错混淆;

renderError

renderError 只在开发者环境下工作,当 render 函数报错时,其错误将会作为第二个参数传递到 renderErrorrenderError 返回的 vnode 将会被渲染。

new Vue({

  render (h) {

    throw new Error('oops')

  },

  renderError (h, err) {

    return h('pre', { style: { color: 'red' }}, err.stack)

  }

}).$mount('#app')

使用场景:renderError可用于开发环节实时把组件错误渲染到页面;

warnHandler

warnHandlererrorHandler一样是全局配置项,但 warnHandler 只在开发者环境下生效,用于捕获 vue 告警。

Vue.config.warnHandler = function (msg, vm, trace) {

  // `trace` 是组件的继承关系追踪

}

使用场景:一般情况下开发者直接在控制台查看 warn,所以 warnHandler 使用场景非常有限。

参考

React,优雅的捕获异常 - 掘金

精读《React Error Boundaries》

React:Suspense 的实现与探讨

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/590902.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

通过符号程序搜索提升prompt工程

原文地址&#xff1a;supercharging-prompt-engineering-via-symbolic-program-search 通过自动探索​​大量提示变体来找到更好的提示 2024 年 4 月 22 日 众所周知&#xff0c;LLMs的成功在很大程度上仍然取决于我们用正确的指导和例子来提示他们的能力。随着新一代LLMs变得越…

django搭建一个AI博客进行YouTube视频自动生成文字博客

文章目录 一、生成Django框架二、项目代码&#xff08;前端&#xff09;1、编写前端代码&#xff08;正文界面&#xff09;1.1、生产html框架1.2、添加live preview扩展1.3、更改title元素中文本1.4、添加CDN&#xff08;CSS&#xff09;样式链接1.5、nav标签1.6、在body标签中…

021、Python+fastapi,第一个Python项目走向第21步:ubuntu 24.04 docker 安装mysql8、redis(二)

系列文章目录 pythonvue3fastapiai 学习_浪淘沙jkp的博客-CSDN博客https://blog.csdn.net/jiangkp/category_12623996.html 前言 安装redis 我会以三种方式安装&#xff0c; 第一、直接最简单安装&#xff0c;适用于测试环境玩玩 第二、conf配置安装 第三、集群环境安装 一…

Flutter笔记:Widgets Easier组件库(9)使用弹窗

Flutter笔记 Widgets Easier组件库&#xff08;9&#xff09;&#xff1a;使用弹窗 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress o…

Redis---------实现更改数据业务,包括缓存更新,缓存穿透雪崩击穿的处理

三种更新策略 内存淘汰是Redis内存的自动操作&#xff0c;当内存快满了就会触发内存淘汰。超时剔除则是在存储Redis时加上其有限期(expire)&#xff0c;有限期一过就会自动删除掉。而主动更新则是自己编写代码去保持更新&#xff0c;所以接下来研究主动更新策略。 主动更新策略…

CSS 鼠标经过放大元素 不影响其他元素

效果 .item:hover{transform: scale(1.1); /* 鼠标悬停时将元素放大 1.1 倍 */ }.item{transition: transform 0.3s ease; /* 添加过渡效果&#xff0c;使过渡更加平滑 */ }

Ubuntu启动后进入GRUB故障-Minimal BASH like line editing is supported.

目录 1.问题描述 2.解决方案 2.1 临时性办法 2.2 工具永久性修复 总结 1.问题描述 PC安装Ubuntu系统第二天重启后提示GUN GRUB version 2.04&#xff0c;之前是WindowsOS装Ubuntu后无法进入图形界面。具体原因据网友提供线索据说是由于在Windows上进行更新/重装/修改了引…

最短路径(朴素)+堆排序+模拟堆

文章目录 Dijkstra求最短路 I堆排序模拟堆 Dijkstra求最短路 I 给定一个 n 个点 m 条边的有向图&#xff0c;图中可能存在重边和自环&#xff0c;所有边权均为正值。 请你求出 1 号点到 n 号点的最短距离&#xff0c;如果无法从 1 号点走到 n 号点&#xff0c;则输出 −1。 输…

2.6Java全栈开发前端+后端(全栈工程师进阶之路)-前端框架VUE3-基础-Vue生命周期

在使用vue进行日常开发中&#xff0c;我们总有这样的需求&#xff0c;想在页面刚一加载出这个表格组件时&#xff0c;就发送请求去后台拉取 数据&#xff0c;亦或者想在组件加载前显示个loading图&#xff0c;当组件加载出来就让这个loading图消失等等这样或那样的需求。 要实…

机器学习之基于Jupyter多种混合模型的糖尿病预测

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景 随着现代生活方式的改变&#xff0c;糖尿病的患病率在全球范围内呈现上升趋势。糖尿病是一种慢性代谢…

k8s笔记 | Ingress

安装Ingress 添加helm创库 Installation Guide - Ingress-Nginx Controller Ingress | Kubernetes 下载包 将 文件helm 放到 /usr/local/bin/ 并给到执行权限 # 添加可执行权限 chmod ux helm # 测试是否能运行 helm version# 结果 version.BuildInfo{Version:"v3.14…

Vue Vant 移动端如何禁止手机调起自带的输入键盘

前言 前不久在公司用Vue2开发了一个手机充值项目&#xff0c;键盘组件用的vant2的NumberKeyboard 数字键盘组件&#xff1b;上线后在IOS端只有一个vant数字键盘组件&#xff0c;但到了Android端&#xff0c;输入框一获取焦点不仅vant数字键盘弹出&#xff0c;连手机自带的键盘…

python基础算法题0502

数字反转 无论是字符串反转还是数字反转&#xff0c;其实都一样。 需求 代码 class Solution:def reverse(self, x: int) -> int:if 0 < x < 2 ** 31 - 1:m str(x)[::-1]if int(m)<2**31-1:return int(m)else:return 0if 0 > x > -2 ** 31:y -xn str(y…

【软件工程】详细设计

目录 前言详细设计算法设计工具——判定表 前言 软件工程生命周期分为八个阶段&#xff1a; 问题定义—>可行性研究—>需求分析 —>概要设计—>详细设计—>编码与单元测试 —>综合测试—>软件维护 这节我们讲的是软件开发流程中的一个阶段&#xff0c;需求…

零代码编程:用Kimichat从PDF文件中批量提取图片

一个PDF文件中&#xff0c;有很多图片&#xff0c;想批量提取出来&#xff0c;可以借助kimi智能助手。 在借助kimi智能助手中输入提示词&#xff1a; 你是一个Python编程专家&#xff0c;要完成一个网页爬取Python脚本的任务&#xff0c;具体步骤如下&#xff1a; 打开文件夹…

【LeetCode刷题】410. 分割数组的最大值

1. 题目链接2. 题目描述3. 解题方法4. 代码 1. 题目链接 410. 分割数组的最大值 2. 题目描述 3. 解题方法 题目中提到的是某个和的最大值是最小的&#xff0c;这种题目是可以用二分来解决的。 确定区间&#xff0c;根据题目的数据范围&#xff0c;可以确定区间就是[0, 1e9]…

【云原生】Docker 实践(五):搭建私有镜像 Harbor

【Docker 实践】系列共包含以下几篇文章&#xff1a; Docker 实践&#xff08;一&#xff09;&#xff1a;在 Docker 中部署第一个应用Docker 实践&#xff08;二&#xff09;&#xff1a;什么是 Docker 的镜像Docker 实践&#xff08;三&#xff09;&#xff1a;使用 Dockerf…

uniapp 应用闪退、崩溃异常日志捕获插件(可对接网络上报)插件 Ba-Crash

应用闪退、崩溃异常日志捕获插件&#xff08;可对接网络上报&#xff09; Ba-Crash 简介&#xff08;下载地址&#xff09; Ba-Crash 是一款uniapp应用闪退、崩溃异常日志捕获插件&#xff0c;支持对接网络上报、设置提示等等&#xff0c;方便对一些远程问题、原生问题进行分…

C# dateTimePicker控件存取数据库问题

存入数据库时&#xff0c;先设置&#xff0c; dateTimePicker1.Format DateTimePickerFormat.Custom; dateTimePicker1.CustomFormat "yyyy-MM-dd HH:mm:ss"; 然后&#xff0c;dateTimePicker1.Text 就和textBox1.Text一样方式存入数据库&#xff1b;…

CMake:静态库链接其他动态库或静态库(九)

1、项目结构 对于下面这样一个项目 把calc模块做成静态或者动态库把sort模块做成静态库然后再sort模块中的*.cpp调用calc模块生成的库即可&#xff08;这样就制作了一个静态库引用动态或者静态库&#xff09;test模块用于测试sort模块中的内容 . ├── calc │ ├── ad…